Patterns and Idioms in the DejaVu Framework

Anton Eliëns, Jacco van Ossenbruggen & Bastiaan Schönhage
Vrije Universiteit, Department of Mathematics and Computer Science,
De Boelelaan 1081, 1081 HV Amsterdam, The Netherlands
email: {eliens,jrvosse,bastiaan}@cs.vu.nl, fax: +31.20.4447653

Abstract

abstract

The DejaVu framework, in particular the hush (hyper utility shell) library and its extensions, aims at providing an easy-to-use and flexible, multi-paradigm environment for developing distributed hypermedia and web-based applications. Our 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 to develop applications and prototypes as well. It is multi-paradigm, not only by supporting a multi-lingual approach, but also by providing support for distributed client/server solutions in a transparent manner.

In this paper we will discuss our experiences in developing the framework with regard to the method of software development employed, recurrent patterns in the design of components, and, on an architectural level, the problems in supporting multiple languages and distribution.

We will discuss the scope of our framework and the approach taken in its realization as a collection of classes. In particular we will delineate the recurrent patterns that we employed in its design and, on an architectural level, the idioms we employed for the concrete realization of the basic concepts underlying the framework. To illustrate the role of the patterns and their associated idioms in our framework, we will discuss the construction of our Web components.

We observe 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 our work, idioms play a central role, that is 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.

Patterns and their associated idioms have guided us in defining an architecture that allows for transparently extending the functionality of our framework. However, with respect to issues of distribution, the idioms employed provide only a partial solution. To realize a framework for distributed applications, we have felt the need to provide additional language support as well, that is by extending existing O-O languages with support for parallelism and distribution.

Our framework has evolved gradually over the years. Reflecting on the approach we have taken and the problems encountered, we will evaluate the relative merits of employing patterns and indicate the costs, in terms of development efforts, incurred by using them in the realization of our framework.

Keywords and phrases: object-oriented framework, multiparadigm, distributed hypermedia, web-based applications, patterns, idioms, languages, development costs

Part of the material presented here has been adapted from Principles of Object-Oriented Software Development by Anton Eliëns, (c) 1995 Addison-Wesley Publishing Co. Inc.

Introduction


introduction framework patterns components conclusions references appendix experience
For both users and developers, delineating the patterns and idioms underlying the realization of an object-oriented framework is slowly being recognized to be of critical importance for understanding the inherent complexity of the framework.

The DejaVu framework, that we are developing, is meant to provide a flexible and easy-to-use environment for intelligent, distributed hypermedia applications. In particular, the hush (hyper utility shell) library and its extensions offer support for the development of multimedia user interfaces, and is used in practical CS2 and CS3 software engineering assignments. The focus of our research has recently shifted towards Web-integrated applications, that allow for including Web-aware widgets as a (GUI) component in an application. In order to realize the functionality of hush in a flexible fashion, a systematic use has been made of a limited number of patterns, namely the nested components pattern and the actor pattern. For the actual implementation hush we employed an improved version of the handle/body idiom, and the derived virtual self-reference and dynamic role-switching idioms that underly the patterns mentioned.

To clarify the distinction between patterns and idioms we emphasize that the patterns provide a solution to a problem of design, whereas idioms are to be regarded as a convenient implementation technique.

Related work

Clearly, a rich variety of patterns and idioms has already been extracted from the development of GUI toolkits as testified by the collection in  [GOF], which is primarily drawn from the experience of developing Interviews  [Interviews] and ET++  [ET]. Nevertheless, our work shows a flaw in one of the commonly used idioms, notably the handle/body idiom, and may further be considered of interest due to our approach of realizing the handle/body idiom and idioms derived from it. Our actor pattern and associated dynamic role switching idiom, moreover, seem to be more powerful than similar patterns known from the literature.

Structure

The structure of this paper is as follows. First, we will give a brief overview of the DejaVu framework and the basic concepts of the hush library. Then we will characterize the patterns and idioms employed in developing hush and its extensions. As an illustration of our approach, we will discuss the design of the Web-related components of our framework. And finally, we will discuss the relative merits of the patterns and idioms employed.
introduction framework patterns components conclusions references appendix experience

The DejaVu framework


introduction framework patterns components conclusions references appendix experience
The problem addressed in the DejaVu project is essentially to provide support for the software engineering of (moderately complex) applications that require, apart from other functionality, multimedia interfaces and associative online help. Such support is primarily offered in the form of class libraries, most of which are written in C++. The language C++ was chosen for pragmatic reasons, since it allows for easily wrapping public domain software written in C in classes with a more user-friendly interface. Another reason was that C++ seemed to be the right vehicle for programming assignments, with regard to its ever more widespread use.

Application development generally encompasses a variety of programming tasks, including system-level software development (for example for networking or multimedia 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  [VE95].

In our project, 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  [Tcl]. Current scripting languages, including Python  [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, that are accessible through any of the script interpreters as well as the C++ interface.

Basic concepts

These concepts are embodied in (pseudo) abstract classes that are realized by employing patterns based on idioms extending the handle/body idiom, as explained later on. 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 (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). The information that results from binding a handler to a script command is stored in an action object, that is used by both widgets and (graphical) items to bind window events (resulting from user actions) to the handler (callback) objects. Both the widget and item class are derived from handler to allow for declaring widgets and items to be their own handler.

The art of hush programming

In fact, every class in the hush library (except for the handler class itself) is derived from the handler class, to allow for defining a script interface for these classes. In a similar way, the extensions to hush are required to define appropriate widget classes, and possibly specialized handler classes and event classes dealing with particular events (such as for example file descriptor events for communicating via sockets or notification events resulting from joining a ToolTalk session  [ToolTalk]). The extensions include 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 the patterns underlying the design of hush and the application of their associated idioms in the realization of the library. In the next section, we will outline the rules of the game.
introduction framework patterns components conclusions references appendix experience

The rules of the game -- patterns and idioms


introduction framework patterns components conclusions references appendix experience
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 extending 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.

The handle/body idiom -- invocation context

The handle/body idiom allows for separating the definition of a component's interface (the handle class) from its realization (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 optimize the creation and destruction of dynamically allocated objects, one may use techniques such as representation or resource sharing and reference counting. However, the use of these techniques can put a heavy burden on the application programmer. The handle/body idiom was introduced in  [Coplien] as a means to hide the use of such low-level issues from the application programmer, by adding a level of indirection, that is delegation to a 'body' implementation object. An additional advantage of applying the idiom is that it allows interface classes to become stable while improving on the realization. In practice, by applying the idiom one may avoid recompilation of dependent code as long as the interface class does not change.

Invocation context

The handle/body idiom is one of the most popular idioms  [Coplien,Eliens95,GOF,Stroustrup,Pree]. It underlies several other idioms and patterns (e.g. the envelope/letter idiom  [Coplien], the Bridge and Proxy patterns  [GOF]). 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.

In our realization of the handle/body idiom the handle class is used both as the interface class to access functionality implemented by its body, and as a semi-abstract base class, defining the interface of the body classes which are derived from the handle. The body object is typically instantiated directly by the constructor of the handle, but this action may be deferred to the moment of first use, or may be done indirectly by using an abstract object factory  [GOF]. To be able to make calls to member functions redefined by user-defined subclasses of the handle class, the body class must make such calls via the handle class as well. See appendix A for a detailed treatment of the idiom and its improvement.

Usage

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 (e.g. to prevent simultaneous use of Motif buttons and OpenLook scrollbars)

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.

Problem

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.

Background

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

Realization

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() member. 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. Note that the realization of the idiom uses inheritance primarily for interface sharing, and not for code sharing and object composition. See appendix B for a detailed code example.

Usage

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.

Problem

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 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.

Background

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  [GOF]. 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.

Realization

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 dictionairy or a simple array of roles will be sufficient. See appendix C for a detailed code example.

Usage

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 Design, 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).
introduction framework patterns components conclusions references appendix experience

Design of Web Components --- an example


introduction framework patterns components conclusions references appendix experience

As an illustration of our approach, we will discuss the design of the Web-related components  [WWW95b] of our framework. The design has been influenced by the following requirements:

The Web Components

Figure 1 shows the design of the basic Web-related components of the hush class library. The browser provides the toplevel user interface for all Web components, including a viewer, scrollbars, navigation buttons (back, forward, home, reload) and an entry box to enter URLs. The client, web and viewer components form the conceptual base of our approach of viewing the Web. Each component can be extended in a simple way, due to a systematic application of the idioms discussed previously.

Multiple viewers are accessible via the MIMEviewer which employs the actor pattern to be able to display various types of MIME-encoded data (e.g. HTML, SGML combined with Tcl style sheets, VRML). The viewer contains a dictionary of several viewer actors, that is indexed on the associated MIME-types. In this way, new viewers can be easily added, even dynamically. The viewer implementation classes can make use of existing widgets to display parts of structured documents, which simplifies their implementation. Another advantage of this design is the fact that whenever a viewer for a new MIME type has been added, it is possible to inline this data type in an SGML or extended-HTML document, via recursive usage of a viewer widget in another viewer. In other words, it is possible to inline VRML or video into an HTML document, or even to insert an HTML-page into another HTML-page. The SGML-viewer uses an SGML-compliant  [SGML] validating parser  [SP] and an (experimental) style sheet mechanism based on the Tcl scripting language. This approach allows for experimenting with new (multimedia) document formats. Since the viewer provides no network functionality at all, it generates an event whenever it needs to retrieve data pointed to by a URL. Such events are generated as a response to user interaction (e.g. clicking an anchor) or to fetch inline data during the parsing process. These events (realized by the hidden urlevent class) are typically handled by a web component.

The client component provides an abstraction of network (file) access and transport protocols. An important consideration in the design of this component was the requirement to allow for new network transfer protocols. This requirement has been met by employing the actor pattern. The client component implicitly changes its role, depending on the protocol required for fetching the data. When the client component gets the instruction to retrieve a URL, it inspects the URL, decides what role it needs and switches to the appropriate role.

The web component combines the functionality of the viewer and client components. It is able to follow links by retrieving data via several protocols (e.g. HTTP, files, FTP), a task delegated to its client component. Additionally, the web component adds a history and caching mechanism to the MIMEviewer. The web widget uses the nested component pattern described in section 3. This means that web inherits the interface of the MIMEviewer and delegates its (viewer) functionality to a inner viewer component. The web component's behavior is similar to the standard widgets of the framework, and can be conveniently used as a part of an application's GUI. Because the web widget has both a C++ interface and a Tcl interface, it is easy to create, or extend, applications with Web functionality.


introduction framework patterns components conclusions references appendix experience

Conclusions


introduction framework patterns components conclusions references appendix experience
Our involvement with patterns and idioms came naturally from our concern to develop a flexible and easy-to-use framework. From its inception we applied idioms (called composition mechanisms in  [Eliens95]) in the realization of the hush library. Reflecting on the design of hush we identified two recurrent patterns, the nested component pattern and the actor pattern, which may be regarded as design level generalizations of their associated idioms.

With respect to meeting the requirements of flexibility and extensibility, our approach has proved its worth. However with respect to the software development efforts involved, the systematic application of the idioms described has drawbacks as well. For example, we found that applying (the naive version of) the handle/body idiom led to disrupting the virtual function call mechanism and hence to inconsistencies in the class hierarchy and additional coding effort. Our improved version solved the first two of these problems. Also, we found that the use of separate body implementation classes gave rise to the introduction of many additional classes. To avoid a pollution of the class name space, we decided to export only a limited number of classes and to employ a suitable idiom for hiding the concrete subclasses.

To evaluate, our experiences are positive, but we had to pay a price. First of all, applying the idioms led to a significantly increased design effort, and secondly, additional coding effort (and discipline) is needed for implementing system level components. In the table below we give a qualitative assessment of how the use of idioms may affect, in our experience, aspects such as reliability, adaptability, maintainability and performance  [Ramp,Eliens95].

Requirement body/handle virtual selfrole-switching
Reliability +/- - -
Adaptability + +/- ++
Maintainability + + -
Performance - - --
With respect to reliability the handle/body idiom may be considered neutral, whereas both the virtual self and role-switching idiom may introduce errors due to their complexity.

No doubt, adaptability is improved by employing the handle/body idiom and even more by employing the role-switching idiom.

The aspect of maintainability is positively affected by applying the handle/body and virtual self idiom, yet moderately negative by role-switching.

In terms of both memory usage and run-time efficiency, loss of performance will result from applying idioms. Apart from the creation and deletion of additional objects, additional object references are required as well as additional de-referencing and, in the case of role-switching, array indexing or hash table search. Nevertheless, the performance loss is certainly not dramatic, in particular with regard to the benefits of having a single uniform interface for the application programmer and at the same time the flexibility needed in a research environment.

Concluding, we have stated our experiences which indicate that patterns are profitable but also that "patterns do not come for free". On the contrary, the employment of patterns and their associated idioms incur significant costs in terms of software development efforts and, in addition, may affect the quality of the software with respect to aspects such as reliability, maintainability and performance.


introduction framework patterns components conclusions references appendix experience

introduction framework patterns components conclusions references appendix experience
Technology, Tools and Applications, April 10-14, 1995, Darmstadt DLP - A language for distributed logic programming, Principles of Object-Oriented Software Development, Hush -- a C++ API for Tcl/Tk, java.sun.com/whitePaper/java-whitepaper-1.html Generalized Markup Language, Distributed Multi-Paradigm Objects, L'Alpe d'Huez, April 3-7, 1995 (availble at www.cs.vu.nl/~martijn/chatting.html)
introduction framework patterns components conclusions references appendix experience

Appendix


introduction framework patterns components conclusions references appendix experience

Appendix


slide: Appendix

The Handle/Body Idiom


introduction framework patterns components conclusions references appendix experience
The handle/body class idiom  [Coplien,Eliens95,GOF,Stroustrup,Pree] 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 several idioms, we use the following class as a running example:
    class A { 
      public:
        virtual void f1()  { cout << "A::f1() "; f2(); }
        virtual void f2()  { cout << "A::f2()" << endl; }
    };
  
The implementation of A is straightforward and does not make use of the handle/body idiom. A call to the {\tt f1()} member function of A will print a message and make a subsequent call to {\tt 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 {  // Handle class delegating to its body
      public:
        A();  // defined later
        virtual ~A() { delete body; }
        virtual void f1() { body->f1(); }
        virtual void f2() { body->f2(); }
      private: A* body;
      protected: A(int dummy);  // used by body
    };
  
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: public A { 
      public:
        BodyOfA();
        void f1()  { cout << "A::f1() "; f2(); }
        void f2()  { cout << "A::f2()" << endl; }
    };
  
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 {\em Abstract Factory\/} pattern described in  [GOF]. may be used to solve this problem in a generic and elegant way. Another (esthetic) problem is the need for the dummy constructor to prevent a recursive chain of constructor calls:
    BodyOfA::BodyOfA(): A(911) { }  // Call dummy constructor of A
    A::A(int dummy) { body = 0; }   // to build an abstract instance of A
    A::A() { body = new BodyOfA; }  // to build a concrete instance of A