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.
- session -- to manage (parts of) the application
- kit -- to provide access to the underlying system and interpreter
- handler -- to bind C++ functionality to events
- event -- stores information concerning user actions or system events
- action -- stores information of the binding of a handler
- widget -- to display information on a screen
- item -- represents an element of a widget
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
- sim -- for discrete event simulation
- hymne -- for sound synthesis and music
- web -- for connecting to the Web
- video -- for digital video
- vr -- for the OpenGL-based VRML extension
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:
- Communication protocols --
The dominant communication protocol of the Web is currently
HTTP. New or enhanced communication [Chatting] and network
transfer protocols will be needed.
- Client-side computing -- With the announcement of Java
and the realization of the HotJava [Java]
browser, client-side computing by means of high-level
scripting languages has been accepted as a powerful and
flexible means to enhance the dynamic capabilities of
Web-browsers.
- Document formats --
While HTML is well suited as a general purpose mark-up
language, more domain specific and more powerful document
formats will be needed, especially when more sophisticated
multimedia structures have to be encoded.
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 self | role-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
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