Object orientation has brought about a radical shift in our notion of software development. The basic mechanisms of object-oriented programming, encapsulation and inheritance, have clear advantages when it comes to data-hiding and incremental development.
Additional keywords and phrases:
generic types, assertions, canonical classes,
event-driven computation
First we will look at some examples in Java,
illustrating the use of inheritance and delegation
for the realization of some simple idioms and patterns.
Then, we will briefly deal with polymorphism in C++,
including the use of assertions that may be used to enforce
contractual obligations.
After discussing some of the idioms and patterns
that have been employed in the hush framework,
we will look more closely at the catalogue of design patterns
introduced in
In this section, we will look at some simple examples in Java that illustrate how we may use the mechanisms of inheritance and (simple) delegation to define objects that have similar functionality but differ in the way that functionality is realized. These examples prepare the way for the more complex idioms and patterns presented later in this chapter.
In the rest of this section we will look briefly at
the polymorphic constructs offered by C++.
We will also study how behavioral conformance can
be enforced in C++ by including invariants and assertions.
These sections may be skipped on first reading.
Inheritance and delegation in Java
Consider the example below,
an envelope class
that offers a message method.
In this form it is nothing but a variation on the hello world
example presented in the appendix.
public envelope() { }
public void message() {
System.out.println("hello ... ");
}
};
We will proceed in three steps: (1) The envelope class will be redesigned so that it acts only as an interface to the letter implementation class. (2) Then we introduce a factory object, that is used to create envelope and letter instances. (3) Finally, we refine the letter class into a singleton class, that prevents the creation of multiple letter instances.
public letter() { }
public void message() {
System.out.println("Message in a letter");
}
};
Factory
STL is supported by C++ compilers that adhere to the C++
standard, including Microsoft Visual C++ and the Cygnus/GNU C++ compilers.
A more extensive discussion of STL is beyond the scope of this
book, but the reader is advised to consult Assertions in C++
Whatever support a language may offer,
reliable software is to a large extent the
result of a disciplined approach to
programming.
The use of assertions has long since been
recognized as a powerful way in which to check whether
the functional behavior of a program corresponds
with its intended behavior.
In effect, many programming language environments
support the use of assertions in some way.
For example, both C and C++ define a macro assert
which checks for the result of a boolean expression
and stops the execution if the expression is false.
In the example below, assertions are used to check for the satisfying of both the pre- and post-conditions of a function that computes the square root of its argument, employing a method known as Newton iteration.
require ( arg >= 0 );
double r=arg, x=1, eps=0.0001;
while( fabs(r - x) > eps ) {
r=x; x=r-((r*r-arg)/(2*r));
}
promise ( r - arg * arg <= eps );
return r;
}
Whereas Eiffel directly supports the use of assertions by allowing access to the value of an instance variable before the execution of a method through the keyword old, the C++ programmer must rely on explicit programming to be able to compare the state before an operation with the state after the operation.
Assertions may also be used to check whether the
object is correctly initialized.
The pre-condition stated in the constructor
requires that the counter must start with
a value not less than zero.
In addition, the constructor checks whether
the class invariant, stated in the (virtual)
member function invariant, is satisfied.
Similarly, after checking whether the post-condition
of the
From a formal perspective, the use of assertions may
be regarded as a way of augmenting the type system supported
by object-oriented languages.
More importantly, from a software engineering perspective,
the use of assertions is a means to enforce contractual obligations.
Canonical class idioms
The multitude of constructs available in C++ to support
object-oriented programming may lead the reader to think
that object-oriented programming is not at all meant
to reduce the complexity of programming but rather to increase it,
for the joy of programming so to speak.
This impression is partly justified, since the number and complexity
of constructs is at first sight indeed slightly bewildering.
However, it is necessary to realize that each of the constructs introduced
(classes, constructors and destructors, protection mechanisms,
type conversion, overloading, virtual functions and dynamic binding)
may in some way be essential to support object-oriented programming
in a type-safe,
and yet convenient, way.
Having studied the mechanisms, the next step is to find proper ways,
recipes as it were, to use these mechanisms.
What we need, in the terminology of
In this section, we will look at the concrete class idiom for C++, which states the ingredients that every class must have to behave as if it were a built-in type. Other idioms, in particular an improved version of the handle/body or envelope/letter idiom that may be used to separate interface from implementation, will be treated in the next section.
Abstract data types must be indistinguishable from built-in types
To verify whether a concrete data type meets the requirements imposed by the specification of the abstract data type is quite straightforward, although not always easy. However, the task of verifying whether a concrete data type is optimally implemented is rather less well defined. To arrive at an optimal implementation may involve a lot of skill and ingenuity, and in general it is hard to decide whether the right choices have been made. Establishing trade-offs and making choices, for better or worse, is a matter of experience, and crucially depends upon the skill in handling the tools and mechanisms available.
When defining concrete data types, the list of requirements defining the canonical class idiom given in slide 2-canonical may be used as a check list to determine whether all the necessary features of a class have been defined. Ultimately, the programmer should strive to realize abstract data types in such a way that their behavior is in some sense indistinguishable from the behavior of the built-in data types. Since this may involve a lot of work, this need not be a primary aim in the first stages of a software development project. But for class libraries to work properly, it is simply essential.
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
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
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.
void eval(string cmd);
string result();
void bind(string name, handler h);
};
int dispatch( event e ); to dispatch events
int operator();
};
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.
...
void bind( handler h );
void bind( string action, handler h );
...
};
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.
operator();
};
In order to illustrate the idiom, we use the following class as a running example:
public A() { }
public void f1() { System.out.println("A.f1"); f2(); }
public void f2() { System.out.println("A.f2"); }
};
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;
};
public BodyOfA() { super(911); }
public void f1() { System.out.println("A.f1"); f2(); }
public void f2() { System.out.println("A.f2"); }
};
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:
public void f2() { System.out.println("C.f2"); }
};
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
};
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.
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;
};
indeed, b
}
};
Those well-versed in design patterns will recognize the Decorator
patterns (as applied in the Interviews MonoGlyph class,
Taking our view of a person as an actor
as a starting point,
we need first to establish the repertoire of possible behavior.
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.
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.
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.
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 is closely
related to the Decorator pattern treated in
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.
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
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).
The pattern schema, or rather a simplified version thereof,
is depicted in slide pattern-schema.
Each pattern must have a name, which acts as a handle
in discussions about the design. Being able to speak about
specific pattern solutions, such as a factory,
greatly facilitates discussions about design.
In this section we will look at a brief overview
of the classification as originally presented in
See Pattern Lectures
If you prefer a more dynamic approach, the prototype
pattern might be better.
A prototype is an object that may be used to create
copies or clones, in a similar way as
instances are created from a class.
However, cloning is much more dynamic, the more so
if delegation is used instead of inheritance to share
resources with some ancestor class.
See section prototypes.
The advantage of using a factory, or any of the other
creational patterns, is that exchanging product families
becomes very easy.
Just look for example at the Java Swing library.
Swing is supported under Unix, Windows and MacOS.
The key to multiple platform support is here, indeed,
the use of factories to create widgets.
Factories are also essential when using CORBA,
simply because calling a constructor is of no use
for creating objects on a remote site.
Closely related to the Composite pattern is
the Flyweight pattern, which is needed when
the number of components grows very large.
In that case, the components themselves must for obvious reasons
carry as little information as possible.
Context or state information must then be passed as a parameter.
To give some more examples, suppose there exists a
nice library for formatting text and images, but unfortunately
with only a procedural interface.
Then the Adaptor pattern may be used to provide
a interface that suits you, by wrapping the original library.
The Bridge pattern is in some sense related to the
Factory. In order to work with a platform-independent
widget library, you need, as has been explained, a factory
to hide the creation of widgets, but you also need to
bridge a hierarchy of platform-dependent implementation
classes to the more abstract platform-independent widget set.
When creating widgets to display text or images it may be very
inconvenient to create a separate class, for example
when adding scrolling functionality.
The Decorator pattern
allows you to insert additional functionality without subclassing.
Now think of a networked application, for example to be able
to incorporate components that are delivered by a server.
The library may provide a number of networking classes
that deal with all possible communication protocols.
To simplify access to these classes a Facade
may be built, hiding the complexity of the original
class interfaces.
Alternatively, remote components may be available
through a proxy.
The Proxy pattern describes how access may be regulated
by an object that acts as a surrogate for the real object.
Like composites and decorators, proxies
may be used for recursive composition.
However, proxies primarily regulate access,
whereas decorators add responsibilities,
and composites represent structure.
As an example of the Template Method pattern,
think of a compiler class that offers methods for
scanning and the creation of a parse tree.
Each of these methods may be refined without affecting the
structure of the compilation itself.
An interpreter allows for evaluating
expressions, for example mathematical formula.
Expressions may be organised in a hierarchy.
new kinds of expressions can be inserted simply by filling
in details of syntax and (semantic) evaluation.
Object composition, which employs the handle/body
idiom and delegation, is employed in
the Mediator pattern,
the Chain of Responsibility pattern
and the Observer pattern.
The actual task, such as for example updating
the display of information when the actual information
has changed, is delegated to a more specialized object,
to achieve a loose coupling between components.
The difference between a mediator and
chain of responsibility is primarily the complexity
of co-ordinating the tasks.
For example, changing the format of a single image component
from one image type to another image type may be done
simply by using an image converter (mediator),
whereas
exporting the complete document to a particular format
such as HTML may involve delegating control to a
specialized converter that itself needs access to
the original components (chain of responsibility).
We will discuss the Observer pattern in more detail later.
As an example of the Command pattern,
think of how you would realize insertion
and deletion commands in an interactive editor,
with undo!
Turning these commands into an object in which the
information necessary for undoing the command can be stored,
for example having a snapshot of the state stored in
a Memento, it suffices to stack the actual
command objects. To undo a command, pop the stack and invoke
the undo method.
The Strategy pattern may be used
to hide the details of the various layout algorithms
that are available. For example, you may use a straightforward
algorithm that formats the text line by line, or you may
use the much more advanced formatting algorithm of \TeX,
which involves the minimalization of penalties.
These alternatives can be collected in a formatting strategy
hierarchy, that hides the implementation details from the client
by a common interface.
When doing the formatting, you may wish
to separate the traversal of the component tree structure
from the actual formatting operations.
This may be accomplished by employing the Visitor
pattern.
In general it is recommended to abstract
from the data structure and use a more abstract
way, such as an Iterator or Visitor
to access and traverse it.
The State pattern is similar to the dynamic role switching idiom
that has been discussed in section hush-patterns 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.
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) { }
};
public void talk() { System.out.println("OOP"); }
public void think() { System.out.println("Z"); }
};
public void talk() { System.out.println("money"); }
public void act() { System.out.println("business"); }
};
public person() {
role = new actor[ Final+1 ];
for( int i = Person; i <= Final; i++ ) role[i]=this;
become(Person);
}
public boolean exists() { return role
public void talk() { System.out.println("interesting"); }
};
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
}
};
The art of hush programming
\label{hush-patterns}
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.
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, Invocation context
The nested component pattern
Nested components
The actor pattern
Actor pattern
A catalogue of design patterns
subsections:
Why patterns, you may wonder.
Why patterns and why not a method of object-oriented design
and an introduction in one or more object-oriented languages?
The answer is simple.
Patterns bookmark effective design.
They fill the gap between the almost infinite possibilities of
object-oriented programming languages and tools
and the rigor of methodical design.
As Brian Foote expressed it in A catalogue of design patterns
Name -- handle
Problem -- when to apply
Solution -- general arrangement
Consequences -- tradeoffs
Creational patterns
Design for change means to defer commitment
to particular object implementations as long as possible.
Due to inheritance, or rather subtyping,
the client, calling a particular method,
can choose the most abstract class, highest in the hierarchy.
However, when it comes to creating objects,
there seems to be no other choice than naming
the implementation class explicitly.
Wrong.
Creational patterns are meant to take care of that,
that is to hide the actual class used as far away as possible.
Creational patterns
Structural patterns
Objects rarely live in isolation.
In slide structural-patterns a selection of the
structural patterns treated in Structural patterns
Pattern Alias Remarks Composite part/whole collections of components Flyweight handle/body extrinsic state, many objects Adaptor wrapper resolve inconsistency between interfaces Bridge handle/body relate abstraction to implementation Decorator handle/body to introduce additional functionality Facade handle/body provides unified interface Proxy handle/body to defer ... remote, virtual, protection Behavioral patterns
Our final category of patterns, behavioral patterns,
concern the construction of algorithms and the assignment
of responsibilities to the objects that cooperate in achieving
some goal.
Behavioral patterns
class
object
Encapsulating behavior
one-to-many dependencies and notification
In effect, MVC or the Observer pattern can be regarded as a simple method for constraint propagation. An advantage is that unexpected updates can be easily dealt with.
In the implementation of the Observer pattern there are a number of problems and tradeoffs that must be considered. For example, do we allow one observer to be attached to more than one subject? Do we allow for alternative update semantics, for example observer-pull instead of subject-push? Do we provide facilities for specifying aspects of interest, so that updates only need to concern those aspects? And finally, how do we guarantee mutual consistency between subjects and observers when we do allow for alternative update semantics?
In this section we will look at the Reactor
pattern that explains the interaction between objects
and the environment.
We will also look at an event system, in
which the event types are defined by the application programmer.
In this application, events are used to maintain global
consistency, similar to the Observer pattern.
The Reactor pattern
The Reactor pattern has been introduced
in
See D.C. Schmidt, Using Design Patterns to Develop Reusable Object-oriented
Communication Software, CACM October '95, 38(10): 65-74
Concrete handlers, derived from an abstract handler,
must provide a method, such as
Consequences
The fact that control is handed over to
the environment has, however, also some disadvantages.
First of all, as experience with student assignments shows,
it is difficult to learn in the beginning.
But even when mastered, applications may be hard
to debug, since it is not always clear why a particular handler
was invoked, and because it may be difficult to repeat
the computation preceding the fault.
Applicability
Abstract event systems
To conclude this chapter about idioms and patterns,
we will look at a somewhat more detailed example
employing (user-defined) events to characterize
and control the interaction between the objects.
The example is taken from
For Functional behavior
protected thermometer( float v ) { temp = v; }
public void set(float v) { temp = v; }
public float get() { return temp; }
protected float temp;
};
public centigrade() { super(0); }
public void set(float v) { temp = v + 273; }
public float get() { return temp - 273; }
};
public fahrenheit() { super(0); }
public void set(float v) { temp = (v - 32) * 5/9 + 273; }
public float get() { return temp * 9/5 + 32 - 273; }
};
User interface
public displayer() { ... }
public void put(String s) { ... }
public void put(float f) { ... }
};
public prompter(String text) { ... }
public float get() { ... }
public String gets() { ... }
};
Events
pubic void dependent(event e) { ... }
pubic void process() { ... }
public void operator(); // abstract method
private event[] dep;
};
public update(thermometer th, prompter p) {
_th =th; _p = p;
}
void operator()() {
_th.set( _p.get() );
process();
}
thermometer _th;
prompter _p;
};
public show(thermometer th, displayer d) {
_th = th; _d = d;
}
public void operator() {
_d.put( _th.get() );
process();
}
thermometer _th;
displayer _d;
};
The installation
Discussion
For an introduction to Java, there is ample choice.
An excellent online tutorial can be found on
java.sun.com/docs/books/tutorial .
As textbooks on C++ I recommend
(C) Æliens 04/09/2009
You may not copy or print any of this material without explicit permission of the author or the publisher. In case of other copyright issues, contact the author.