Principles of Object-Oriented Software Development
[] readme course preface 1 2 3 4 5 6 7 8 9 10 11 12 appendix lectures resources

talk show tell print

Idioms in hush

subsections:


The hush framework, developed by the author and his colleagues, aims at providing an easy-to-use and flexible, multi-paradigm environment for developing distributed hypermedia and web-based applications. Actually hush, which stands for hyper utility shell, is a part of the DejaVU framework which has been developed at the Free University in Amsterdam over the last five years. The DejaVU framework is meant as an umbrella for our research in object-oriented applications and architectures. Many of the examples in this book are in some way derived from hush or applications developed within the DejaVU project. The hush library was originally developed in C++, but parts of it have been ported to Java using the Java native runtime interface. You will see examples of hush in chapters 4, 6, 7, 11 and 12.

In this section a brief overview will be given of the basic concepts underlying hush. Then we will discuss the idioms used in realizing hush and its extensions, in particular an adapted version of the handle/body idiom originally introduced in  [Coplien92], the virtual self-reference idioms and the dynamic role switching idiom. At the end of this section we will discuss the implications these idioms have for developing hush applications. Readers not interested in hush may safely skip the introduction that follows and the discussion at the end of this section.

The hush framework is object-oriented in that it allows for a component-wise approach to developing applications. Yet, in addition to object class interfaces, it offers the opportunity to employ a script language, such as Tcl and Prolog, to develop applications and prototypes. The hush framework is a multi-paradigm framework, not only by supporting a multi-lingual approach, but also by providing support for distributed client/server solutions in a transparent (read CORBA) manner.

In this section we will look at the idioms employed for the realization of the framework. In developing hush we observed that there is a tension between defining a clean object model and providing the flexibility needed to support a multiparadigm approach. We resolved this tension by choosing to differentiate between the object model (that is class interfaces) offered to the average user of the framework and the object model offered to advanced users and system-level developers.

In this approach, idioms play a central role. We achieved the desired flexibility by systematically employing a limited number of basic idioms. We succeeded in hiding these idioms from the average user of the framework. However, the simplicity of our original object model is only apparent. Advanced or system-level developers who intend to define extensions to the framework must be well aware of the patterns underlying the basic concepts, that is the functionality requirements of the classes involved, and the idioms employed in realizing these requirements.

The hush framework -- basic concepts

Application development generally encompasses a variety of programming tasks, including system-level software development (for example for networking or multi-media functionality), programming the user interface (including the definition of screen layout and the responsivity of the interface widgets to user actions), and the definition of (high-level) application-specific functionality. Each of these kinds of tasks may require a different approach and possibly a different application programming language. For example, the development of the user interface is often more conveniently done using a scripting language, to avoid the waiting times involved in compiling and linking. Similarly, defining knowledge-level application-specific functionality may benefit from the use of a declarative or logic programming language.

In developing hush, we decided from the start to support a multiparadigm approach to software development and consequently we had to define the mutual interaction between the various language paradigms, as for example the interaction between C++ and a scripting language, such as Tcl. Current scripting languages, including Python and Tcl, provide facilities for being embedded in C and C++, but extending these languages with functionality defined in C or C++ and employing the language from within C/C++ is rather cumbersome. The hush library offers a uniform interface to a number of script languages and, in addition, it offers a variety of widgets and multimedia extensions, which are accessible through any of the script interpreters as well as the C++ interface. These concepts are embodied in (pseudo) abstract classes that are realized by employing idioms extending the handle/body idiom, as explained later on.


Basic hush classes


slide: Basic hush classes

Programming a hush application requires the definition of an application class derived from session to initialize the application and start the (window environment) main loop. In addition, one may bind Java or C+= handler objects to script commands by invoking the kit::bind function. Handler objects are to be considered an object realization of callback functions, with the advantage that client data may be accessed in a type-secure way (that is either by resources stored when creating the handler object or by information that is passed via events). When invoked, a handler object receives a pointer to an event (that is, either an X event or an event related to the evaluation of a script command). Both the widget and (graphical) item class are derived from handler to allow for declaring widgets and items to be their own handler.

Embedding script interpreters

The hush framework offers a generic kit that may be used as the interface to any embedded interpreter. The public interface of the kit class looks as follows:


    interface kit { 
kit

void eval(string cmd); string result(); void bind(string name, handler h); };
The function eval is used for evaluating (script) commands, and result may be used to communicate data back. The limitation of this approach, obviously, is that it is purely string based. In practice, however, this proves to be flexible and sufficiently powerful. The bind function may be used to define new commands and associate it with functionality defined in handler objects, which are introduced below.

Handler objects

The problem of extending the script language with functionality defined by the application, is (as already indicated above) addressed by defining a generic handler object class. Handler objects may be regarded as a generalization of callback functions, in the sense that they are activated whenever the corresponding script command is evaluated. The advantage of using objects for callbacks instead of functions, obviously, is that we no longer need type-insecure casts, or static or global variables to pass information around. The public interface of the handler class looks as follows:


     interface handler { 
handler

int dispatch( event e );
// to dispatch events

int operator(); };
The dispatch function is called by the underlying system. The dispatch function receives a pointer to an event which encodes the information relevant for that particular callback. In its turn dispatch calls the operator() function. Classes derived from handler need only redefine the operator() function. Information needed when activating a handler object must be provided when creating the object, or obtained from the event for which the handler is activated.

The use of handler objects is closely connected to the paradigm of event-driven computation. An event, conceptually speaking, is an entity that is characterized by two significant moments, the moment of its creation and the moment of its activation, its occurrence. Naturally, an event may be activated multiple times and even record a history of its activation, but the basic principle underlying the use of events is that all the information that is needed is stored at creation time and, subsequently, activation may proceed blindly. See section reactor.

User actions

Another use of handler objects (in hush) is for defining what must be done in response to user events, resulting from actions such as moving the mouse, or pressing a button, or selecting an entry from a menu. This is illustrated by the public interface of the generic widget class:


     interface widget : handler { 
widget

... void bind( handler h ); void bind( string action, handler h ); ... };
The first member function bind may be used for installing a handler for the default bindings of the widget, whereas the second bind function is to be used for overriding any specific bindings. (Recall that the class widget is derived from handler class to allow the widget to be its own handler. In this way inheritance or the delegation to a separate handler object may be used to define the functionality of a widget.)

In addition to the widget class, the hush library also provides the class item, representing graphical items. Graphical items, however, are to be placed within a canvas widget, and may be tagged to allow for the groupwise manipulation of a collection of items, as for example moving them in response to dragging the mouse pointer.

Programmer-defined events

User interface events occur in response to actions by the user. They are scheduled by the underlying window system, which invokes the handler whenever it is convenient or necessary. When getting used to event-driven computation, system designers and programmers may feel the need to have events at their disposal that may be scheduled at will, under the programmer's control. It will come as no surprise that another use of handler objects is to allow for programmer-defined events. The public interface of the class event looks as follows:


     interface event : handler { 
event

operator(); };
Actual event classes are derived from the generic class event, and a scheduler is provided to activate events at the appropriate time. (In effect, we provide a fully functional discrete event simulation library, including facilities for generating random distributions and analysing the outcome of experiments. Business process simulations done with this library are discussed in Chapter 11.) Note that there is an important difference between programmer-defined events and system-defined events. System-defined events are delivered to the user by activating a handler callback. In contrast, programmer-defined events are (directly) activated by a scheduler. They contain, so to speak, their own handler.

Discussion

What benefits do we derive from employing handler objects and their derivatives? One advantage is that we have a uniform way to define the functionality of script commands, callbacks to user actions and programmer-controlled events. Another, less apparent advantage, is that it allows us to incorporate a variety of functionality (including sound synthesis facilities, digital video and active documents) in a relatively straightforward fashion.

The handle/body idiom

The handle/body class idiom, originally introduced in  [
Coplien92], separates the class defining a component's abstract interface (the handle class) from its hidden implementation (the body class). All intelligence is located in the body, and (most) requests to the handle object are delegated to its implementation.

In order to illustrate the idiom, we use the following class as a running example:



  class A { 
A -- naive

public A() { } public void f1() { System.out.println("A.f1"); f2(); } public void f2() { System.out.println("A.f2"); } };

slide: Running example

The implementation of A is straightforward and does not make use of the handle/body idiom. A call to the f1() member function of A will print a message and make a subsequent call to f2(). Without any modification in the behavior of A's instances, it is possible to re-implement A using the handle/body idiom. The member functions of class A are implemented by its body, and A is reduced to a simple interface class:


  class A { 
A

public A() { body = new BodyOfA(this); } protected A(int x) { } public void f1() { body.f1(); } public void f2() { body.f2(); } public void f3() { System.out.println("A.f3"); } private A body; };

slide: Interface: A

Note that the implementation of A's body can be completely hidden from the application programmer. In fact, by declaring A to be the superclass of its body class, even the existence of a body class can be hidden. If A is a class provided by a shared library, new implementations of its body class can be plugged in, without the need to recompile dependent applications:


  class BodyOfA extends A { 
BodyOfA -- naive

public BodyOfA() { super(911); } public void f1() { System.out.println("A.f1"); f2(); } public void f2() { System.out.println("A.f2"); } };

slide: Naive: BodyOfA

In this example, the application of the idiom has only two minor drawbacks. First, in the implementation below, the main constructor of A makes an explicit call to the constructor of its body class. As a result, A's constructor needs to be changed whenever an alternative implementation of the body is required. The Abstract Factory pattern described in  [GOF94] may be used to solve this problem in a generic and elegant way. Another (aesthetic) problem is the need for the dummy constructor to prevent a recursive chain of constructor calls.

But the major drawback of the handle/body idiom occurs when deriving a subclass of A which partially redefines A's virtual member functions. Consider this definition of a derived class C:



  class C extends A { 
C

public void f2() { System.out.println("C.f2"); } };

slide: Usage: C

Try to predict the output of a code fragment like:


    C c = new C; c.f1();    
  

slide: Example: calling C

The behavior of instances of C does indeed depend on whether the hidden implementation of its base class A applies the handle/body idiom or not! If it does, the output will be A.f1() A.f2(). because the indirect call to f2() in f1() will (unexpectedly) not call the redefined version of f2(). The original definition of A would of course yield A.f1() C.f2(). but this can only be obtained by deriving C directly from the (hidden) body class. Note that this is an illustration of one of the main drawbacks of the OOP paradigm: the inability to change base classes at the top of a hierarchy without introducing errors in derived classes.

Explicit invocation context

In both implementations of A, the call to f2() in f1() is an abbreviation of this.f2(). However, in the first, naive implementation of A, the implicit this reference refers to the handle object (which can be an instance of a derived class). In contrast, this in the BodyOfA will refer to the body object. As a consequence, the body object is unable to make calls to functions redefined by classes derived from the base class A. We use the term invocation context to denote a reference to the context in which the original request for a specific service is made, and represent this by a pointer to the handle object. In other words, the handle object needs a pointer to its body to be able to delegate its functionality, and, symmetrically, the body needs a pointer to the handle in order to be able to use any redefined virtual functions. The body can be redefined as:


  class BodyOfA extends A { 
BodyOfA

public BodyOfA(A h) { super(911); handle = h; } public void f1() { System.out.println("A.f1"); handle.f2(); } public void f2() { System.out.println("A.f2"); } A handle; // reference to invocation context };

slide: Handle/Body: BodyOfA

The new body class is aware of the fact that it is implementing services which are accessed via the handle object. Consequently, it can use this information and is able to make calls to functions which might be redefined by descendants of A.

Note that this solution does require some programming discipline: all (implicit) references to the body object should be changed into a reference to the invocation context. Fortunately, this discipline is only required in the body classes of the implementation hierarchy.



slide: Separating interface hierarchy and implementation

Descendants of the handle classes in the public interface hierarchy can share and redefine code implemented by the hidden body classes in a completely transparent way, because all code sharing takes place indirectly, via the interface provided by the handle classes. However, even other body classes will typically share code via the handle classes. Also derived classes can use the handle/body idiom, as depicted in slide handle-body.

Virtual self-reference

A special feature of the hush widget class library is its support for the definition of new composite widgets, which provide to the application programmer the interface of a built-in (or possibly other composite) widget. To realize this flexibility, we introduced a self() function that adds another level of indirection to self-reference. For example, look at the item class below:


  class item { 
item

public item(String x) { _name = x; _self = null; } String name() { return exists()?self().name():_name; } public void redirect(item x) { _self = x; } boolean exists() { return _self != null; } public item self() { return exists()?_self.self():this; } item _self; String _name; };

slide: Item with self()

The item class has an instance variable _self, that can be set to an arbitrary instance of item by invoking redirect. Now, when we ask for the name of the item, it is checked whether a redirection exists. If so, the call is redirected to the instance referenced by self(), otherwise the name of the item itself is returned.


  public class go {
  
  public static void main(String[] args) {
  	item a = new item("a");
  	item b = new item("b");
  	a.redirect(b);
  	System.out.println(a.name()); 
indeed, b

} };

slide: item: go

In combination with the handle/body idiom, we can create composites offering the interface of item, providing access to one or more (inner) items. This will be further illustrated in chapter 4.

Those well-versed in design patterns will recognize the Decorator patterns (as applied in the Interviews MonoGlyph class,  [Interviews]).

Dynamic role-switching

For many applications, static type hierarchies do not provide the flexibility needed to model dynamically changing roles. For example we may wish to consider a person as an actor capable of various roles during his lifetime, some of which may even coexist concurrently. The characteristic feature of the dynamic role switching idiom underlying the actor pattern is that it allows us to regard a particular entity from multiple perspectives and to see that the behavior of that entity changes accordingly. We will look at a possible realization of the idiom below.

Taking our view of a person as an actor as a starting point, we need first to establish the repertoire of possible behavior.



  class actor { 
actor

public static final int Person = 0; public static final int Student = 1; public static final int Employer = 2; public static final int Final = 3; public void walk() { if (exists()) self().walk(); } public void talk() { if (exists()) self().talk(); } public void think() { if (exists()) self().think(); } public void act() { if (exists()) self().act(); } public boolean exists() { return false; } public actor self() { return this; } public void become(actor A) { } public void become(int R) { } };

slide: actor.java

Apart from the repertoire of possible behavior, which consists of the ability to walk, talk, think and act, an actor has the ability to establish its own identity (self) and to check whether it exists as an actor, which is true only if it has become another self. However, an actor is not able to assume a different role or to become another self. We need a person for that!

Next, we may wish to refine the behavior of an actor for certain roles, such as for example the student and employer roles, which are among the many roles a person can play.



  class student extends actor { 
student

public void talk() { System.out.println("OOP"); } public void think() { System.out.println("Z"); } };

  class employer extends actor { 
employer

public void talk() { System.out.println("money"); } public void act() { System.out.println("business"); } };

slide: Students and Employers

Only a person has the ability to assume a different role or to assume a different identity. Apart from becoming a Student or Employer, a person may for example become an adult_person and in that capacity again assume a variety of roles.

  class person extends actor { 
person

public person() { role = new actor[ Final+1 ]; for( int i = Person; i <= Final; i++ ) role[i]=this; become(Person); } public boolean exists() { return role[_role] != this; } public actor self() { if ( role[ Person ] != this ) return role[ Person ].self(); else return role[_role]; } public void become(actor p) { role[ Person ] = p; } public void become(int R) { if (role[ Person ] != this) self().become(R); else { _role = R; if ( role[_role] == this ) { switch(_role) { case Person: break; // nothing changes case Student: role[_role] = new student(); break; case Employer: role[_role] = new employer(); break; case Final: role[_role] = new actor(); break; default: break; // nothing happens } } } } int _role; actor role[]; };

slide: person.java

A person may check whether he exists as a Person, that is whether the Person role differs from the person's own identity. A person's self may be characterized as the actor belonging to the role the person is playing, taking a possible change of identity into account. When a person is created, his repertoire is still empty. Only when a person changes identity by becoming a different actor (or person) or by assuming one of his (fixed) roles, is he capable of displaying actual behavior.

Assuming or `becoming' a role results in creating a role instance if none exists and setting the _role instance variable to that particular role. When a person's identity has been changed, assuming a role affects the actor that replaced the person's original identity. (However, only a person can change roles!)

The ability to become an actor allows us to model the various phases of a person's lifetime by different classes, as illustrated by the adult class.



  class adult extends person {  
adult

public void talk() { System.out.println("interesting"); } };

slide: adult.java

In the example code below we have a person talking while assuming different roles. Note that the person's identity may be restored by letting the person become its original self.


  public class go { 
example

public static void main(String[] args) { person p = new person(); p.talk(); // empty p.become(actor.Student); p.talk(); // OOP p.become(actor.Employer); p.talk(); // money p.become(new adult()); p.talk(); // interesting p.become(actor.Student); p.talk(); // OOP p.become(p); p.talk(); // old role: employer p.become(actor.Person); p.talk(); // initial state } };

slide: go.java

The dynamic role switching idiom can be used in any situation where we wish to change the functionality of an object dynamically. It may for example be used to incorporate a variety of tools in a drawing editor, as illustrated in chapter 4.

The art of hush programming

For the average user, programming in hush amounts (in general) to instantiating widgets and appropriate handler classes, or derived widget classes that define their own handler. However, advanced users and system-level programmers developing extensions are required to comply with the constraints resulting from the patterns underlying the design of hush and the application of their associated idioms in the realization of the library.

The design of hush and its extensions can be understood by a consideration of two basic patterns and their associated idioms, that is the nested-component pattern (which allows for nesting components that have a similar interface) and the actor pattern (which allows for attributing different modes or roles to objects). The realizations of these patterns are based on idioms that extend an improved version of the familiar handle/body idiom. Our improvement concerns the introduction of an explicit invocation context which is needed to repair the disruption of the virtual function call mechanism caused by the delegation to `body implementation' objects.

In this section, we will first discuss the handle/body idiom and its improvement. Then we will discuss the two basic patterns underlying the design of hush and we will briefly sketch their realization by extensions of the (improved) handle/body idiom.

Invocation context

The handle/body idiom is one of the most popular idioms. It underlies several other idioms and patterns (e.g. the envelope/letter idiom,  [
Coplien92]; the Bridge and Proxy patterns,  [GOF94]).

Invocation context

handle/body


Problem
Inheritance breaks with handle/body
Background
Envelope/Letter, hiding implementations
Realization
Explicit invocation contact in body
Usage
sessions, events, kits, widgets, items

slide: Invocation context

However, despite the fact that it is well documented there seems to be a major flaw in its realization. Its deficiency lies in the fact that the dynamic binding mechanism is disrupted by introducing an additional level of indirection (by delegating to the `body' object), since it is not possible to make calls to member functions which are refined by subclasses of the (visible) handle class in the implementation of the (hidden) body class. We restored the working of the normal virtual function mechanism by introducing the notion of explicit invocation context. In this way, the handle/body idiom can be applied completely transparently, even for programmers of subclasses of the handle.

The (improved version of) the idiom is frequently used in the hush class library. The widget library is build of a stable interface hierarchy, offering several common GUI widgets classes like buttons, menus and scrollbars. The widget (handle) classes are implemented by a separate, hidden implementation hierarchy, which allows for changing the implementation of the widget library, without the need to recompile dependent applications. Additionally, the idiom helps us to ensure that the various widget implementations are used in a consistent manner.

The nested component pattern

The nested component pattern has been introduced to support the development of compound widgets. It allows for (re)using the script and C++ interface of possibly compound widgets, by employing explicit redirection to an inner or primary component.

Nested components

virtual self-reference


Problem
Realizing composites with single inheritance
Background
Decorators, prototypes
Realization
Smart delegation
Usage
Composite widgets, embedded logic

slide: Nested components

Inheritance is not always a suitable technique for code sharing and object composition. A familiar example is the combination of a Text object and two scrollbars into a ScrollableText object. In that case, most of the functionality of ScrollableText will be equal to that of the Text object. This problem may be dealt with by employing multiple inheritance. Using single inheritance, it may be hard to inherit this functionality directly and add extra functionality by attaching the scrollbars, especially when interface inheritance and implementation inheritance coincide.

The nested component pattern is closely related to the Decorator pattern treated in  [GOF94] and InterViews' notion of MonoGlyph,  [Interviews]. Additionally, by using explicit delegation it provides an alternative form of code sharing to inheritance, as can be found in languages supporting prototypes or exemplars, see section prototypes.

The nested component pattern is realized by applying the virtual self-reference idiom. Key to the implementation of that idiom is the virtual self() member of a component. The self() member returns a reference to the object itself (e.g. this in C++) by default, but returns the inner component if the outer object explicitly delegated its functionality by using the redirect() method. Note that chasing for self() is recursive, that is (widget) components can be nested to arbitrary depth. The self() member must be used to access the functionality that may be realized by the inner component.

The nested component pattern is employed in designing the hush widget hierarchy. Every (compound) widget can delegate part of its functionality to an inner component. It is common practice to derive a compound widget from another widget by using interface inheritance only, and to delegate functionality to an inner component by explicit redirection.

The actor pattern

The actor pattern provides a means to offer a multitude of functional modes simultaneously. For example, a single kit object gives access to multiple (embedded) script interpreters, as well as (possibly) a remote kit.

Actor pattern

dynamic role switching


Problem
Static type hierarchies may be too limited
Background
State transitions, self-reference
Realization
Dynamic instantiation and delegation
Usage
Web viewer, kit -- embedded logic

slide: Actor pattern

The characteristic feature of the actor pattern is that it allows us to regard a particular entity as being attributed various roles or modes and that the behavior of that entity changes accordingly.

Changing roles or modes can be regarded as some kind of state transition, and indeed the actor pattern (and its associated dynamic role-switching idiom) is closely related to the State pattern treated in  [GOF94]. In both cases, a single object is used to access the current role (or state) of a set of several role (or state) classes. In combination with the virtual self-reference idiom, our realization of the actor pattern allows for changing the role by installing a new actor.

The realization of the actor pattern employs the dynamic role-switching idiom, which is implemented by extending the handle class with a set of several bodies instead of only one. To enable role-switching, some kind of indexing is needed. Usually, a dictionary or a simple array of roles will be sufficient.

In the hush library the actor pattern is used to give access to multiple interpreters via the same interface class (i.e. the kit class). The pattern is essential in supporting the multi-paradigm nature of the DejaVU framework. In our description of the design of the Web components in section Web, we will show how dynamic role-switching is employed for using various network protocols via the same (net)client class. The actor pattern is also used to define a (single) viewer class that is capable of displaying documents of various MIME-types (including SGML, HTML, VRML).



[] readme course preface 1 2 3 4 5 6 7 8 9 10 11 12 appendix lectures resources
eliens@cs.vu.nl

draft version 0.1 (15/7/2001)