Patterns

Last Updated: 1999

The Book:

Design Patterns: Elements of Reusable Object-Orientated Software
    Erich Gamma, Richard Helm, Ralph Johnson & John Vissides

The Shocker:

"Favour object composition over class inheritance"

  1. Overridden methods in subclasses often require exceptional knowledge of the workings of the super-class (for example, how other routines in the super-class are implemented). This breaks encapsulation and increases maintenance and debugging problems.
     
  2. Object composition can be changed at run-time making them more flexible (and removing the vtable overhead (or whatever mechanism your language of choice uses to implement polymorphism)).
     
  3. A subclass is more difficult to reuse in a new environment often requirement many small changes to code throughout the class hierarchy. With object composition components are more easily reused and reassembled with only the problem component requiring re-coding (or a new one written).
     
  4. Putting (3) another way: There are less implementation dependencies with object composition.

Delegation:

Delegation is often used to replace behaviour normally implemented as inheritance. To rephrase the example in the book:

Inheritance:
Window is subclassed from Rectangle because a Window's are rectangular. Window implements it's DrawArea() by not overriding Rectangle's DrawArea(). Later, when circular windows are required, Window's DrawArea is overridden with custom code which is made difficult to write / confusing to read because it is descended from Rectangle. At run-time, the system does not care whether the Window is rectangular or circular as polymorphism automatically takes care of it.

Delegation:
Window contains a reference to a Rectangle. When Window's DrawArea() function is called, Window delegates this to Rectangle's DrawArea() function. Later, when circular windows are required, Window replaces Rectangle with Circle (which has the same interface as Rectangle) and the delegation now results in Circle's DrawArea() being called. At run-time, the system does not care whether the Window is rectangular or circular as this is taken care of by the class. In fact, the Window can change between Rectangle and Circle at run-time!

 

The disadvantages of Inheritance is that the sort of dynamic structures is generates are more difficult to read and harder to debug. It may also have run-time overheads that are higher than polymorphism (since it is "attached" to the class rather than embedded issues such as garbage collection and instantiation may be more complicated). For these reasons, the Book recommends delegation only be used in highly stylised and standard ways - i.e. in patterns :-).

 

Definitions:
    Aggregation   - One object owns another
    Acquaintance - One object knows of another

 

Creational Patterns

Abstract Factory

"Provide an interface for creating families of related or dependent objects without specifying their concrete classes"

Example:

Creating platform specific implementations of standard UI items (such as buttons and textboxes) without knowing the target platform.

 

Builder

"Separate the construction of a complex object from its representation so that the same construction process can create different representations."

Example:

We have a set of data where each item if data can have zero or more child data items. If we ask a builder to supply the UI for viewing this data it could choose between:

 

Factory Method

(Note: Factory Method, not class. Its all done in a fn, not necessarily in a separate object (thats Abstract Factory). Think hooks)

Example:

MFC style framework. The application framework knows that it should create a new document as a result of the user clicking new under the file menu or off the toolbar. However, since the document is application specific its not possible for the framework to know about a particular subclass of it.

The Factory Method fixes this by encapsulating the creation details of the document into a subclass outside of the framework (but still descended from a class within the framework, obviously):

Application is defined with an overridiable fn, NewDocument(). This is implemented in Application as "return new Document();" and in myApplication as "return new myDocument();"

NewDocument() is a Factory method.

 

Prototype

"Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype"

Note: Not sure what use this is with C# as we can instantiate an object by its class name.

Example:

Implement a clone() interface on all the required objects then (if there is a large number of classes) register an instance of each object. When a new object is requested by name we can look up the existing registered instance of the object (the Prototype) and get a new instance by calling the Clone() method.

 

Singleton

"Ensure a class only has one instance, and provide a global point of access to it"

Example:
Singletons are used where:

  1. There must be exactly one instance of a class
  2. The class must have a well known access point
  3. The instance must be sub-classable and the new class must be able to completely replace the original class without any change to the client code.

Typical uses for a Singleton include: Printer spooler, File system, Window manager.

Implementation:

The Singleton includes an Instance() method that returns either a reference to the existing singleton or creates the singleton and then after returns a reference to the singleton. Obviously to call Instance() without having the singleton already created the method has to be static, for example:

class mySingleton {
    private static mySingleton _mySingleton = null;
    static public mySingleton Instance() {
        if (_mySingleton == null) _mySingleton = new mySingleton();
        return _mySingleton;        // since this is a static class we can't return "this"
    }
}

We then access the singleton only through the Instance method, e.g. mySingleton.Instance().DoSomething();

Note #1:
Personally I prefer:

class mySingleton {
    private static mySingleton _oInstance = null;
    public static mySingleton oInstance {
        if (_oInstance == null) _oInstnace = new mySingleton();
        return _oInstance;
    }
}

as this lets us remove the brackets from what isn't really a function anyway, e.g. mySingleton.Instance.DoSomething().

Note #2:
Singletons are often uses with protected constructors (else clients could work around the "highlander" requirement - there can only be one!)

Note #3:
In C++, some people prefer to return a pointer than the object.
 

Structural Patterns

Adapter (Wrapper)

"Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces."

Allows one class to use another (by wrapping the other's interface to proved a new interface as expected by the client).

Example:

Square and Triangle are descended from the Shape class. You need to add Circle but want to reuse OldCircle from a previous project. The problem is OldCircle is not derived from Shape.

Solution: Derive Circle from Shape and use the Adapter pattern to make Circle encapsulate OldCircle and handle the differences in calls
 

Bridge

"De-couple an abstraction from its implementation so that the two can vary independently".

Related to "Favour Composition Over Inheritance".

Example:


 

 

Composite

"Composes objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects informally".

"The key to the composite pattern is an abstract class that represents both primitives and their containers".

Very straightforward stuff once you remove the jargon:

 

Decorator

"Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to sub-classing for extending functionality"

A Decorator conforms to the interface of the component it decorates so that its presence is transparent to the component's clients. This does not preclude the decorator extending the interface (e.g. aScrollDecorator could add SetScrollPosition()), however if it does this could cause problems when the decorators are "stacked" on top of each other..

Examples:

aTextView might have aScrollDecorator and/or aBorderDecorator applied to it.

 

Facade

"Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use."

An interface for an interface, normally to simplify matters.

Example:

CDC vs. MAPI.

Note:
It is rare for a Facade pattern to require state. Facades are often implemented using the Singleton pattern.

 

Flyweight

"Use sharing to support large numbers of fine-grain objects effeciently"

Example:

An object-orientated word processor might store everything as (nested) objects. This is fine for tables and pictures, but imaging the overhead every character in the document was stored as a separate object.

The Flyweight pattern works by dividing the object's properties / state / info into two categories: Intrinsic and Extrinsic.

Intrinsic are the context independent information like how to draw the letter "a". Extrinsic is context dependent information like "this line is shown in bold".

Since there are comparatively few distinct objects when only the Intrinsic properties are considered these objects can be instantiated and their container supply the Extrinsic information at run-time. For example, rather than:

            Document
           /   |    \
        Row   Row    Row
            / /|\ \
           H E L L O

we would use:

            Document
           /   |    \
        Row   Row    Row
            / / |\ \
           H E   L  O        \
                             |
        A B C D   F G   I    >  Flyweight pool
        J K   M N   P Q R    |
        S T U V W X Y Z      /

with the extrinsic information coming from Row, e.g.

Note #1:
Obviously the clever bit is mapping letters to the letters pool.

Note #2:
The singleton pattern can be used to decrease costs further by only fully instantiating pool objects that are actually referenced.


Proxy

"Provide a surrogate or placeholder for another object to control access to it"

Examples:

 

Behavioural Patterns

Chain Of Responsibility

"Avoid coupling the sender of a request to it's receiver by giving more than one object a change to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it."

Example:

Shift-F1 style help could pass the help request up the from the selected control until a container / control with a help topic is found.

Note #1:
Easy to implement if already using the composite patter (as, for example, HelpHandler() can be part of the interface and the default implementation written to automatically pass messages up the chain). Its harder to implement if chaining non-generically based classes. In these cases, implement it as an interface and pass register the control with a separate Chain Of Responsibility registration object.

Note #2:
Nothing the Chain of Responsibility pattern guarantees the message will be handled.

 

Command

"Encapsulate a request as an object, thereby letting you parameterise clients with different requests, queue or log requests, and support undoable actions."

Examples:

Note:
To provide an undo feature include enough information in the control to undo the action (i.e. 'This is the text I deleted') rather than trying to rebuild it from actions further down the stack (i.e. instead of a journal file approach).

 

Interpreter

"Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language"

Examples:

Note:
This is pattern really used for parsing complex expressions / patterns such as regular expressions or syntax checking. Skipped for now.

 

Iterator

"Provide a way to access the elements of an aggregate object sequentially without exposing it's underlying representation"

or:

How to walk a hierarchy without exposing the internal structures used to implement the hierarchy, yet still be pseudo efficient because we have a concrete iterator per class.

Example:

A tree structure where some items are nodes and some are leafs. The iterator pattern allows for multiple types of node and leaf if required.

Every iterator has (at least) the following methods implemented in its iterator:

First()            Move to the first item (at that "level" or node)
Next()           Move to the next item
IsDone()       Are there anymore items?
GetCurrent() Return a reference to the current node or leaf

Obviously, for this to work we also need for the implementation of the iterator to have state (specifically to record where in the list we are else the "Next()" function hasn't a hope) and for all the objects in the tree to castable to the same base type (i.e. leaf and nodes both inherit from "treeObject").

Lastly, every subclass of treeObject must implement a CreateIterator function.

The examples in the book cheat because they do not handle branching structures (i.e. no child data) and as such to support the Next statement the only information they have to support is the current position in the list. In reality all node iterators would be holding a reference to another iterator and passing most calls to the Next() and IsDone() through to it as well as all calls to GetCurrent ().

<pic>

We would therefore implement something like:

NormalNodeIterator::Next()    Move through items in its internal list, calling that item's CreateConstructor
LeafIterator::Next()                 Set IsDone=.T. and fail (AKA a "Null" iterator)
etc.

Note #1:
A Robust itereator is one that can survive adds & deletes during iteration

Note #2:
Lack of access to the internals of the class being iterated can be a problem (!), thus concrete iterators are normally made a friend class of the class being iterated.

Note #3:
Don't forget to delete iterators you've finished with

 

 

Mediator

"Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently"

Examples:

A form with many controls will typically have interaction between controls, for example a login form may not enable the <ok> button until the username and password textboxes are filled in. Rather than have every control talking to (and knowing about) every other control on the form (a many-to-many relationship) we can have each control only knowing about the Mediator object (aka the Director object) and the Mediator object knowing all the involved controls (a series of many-to-one and one-to-many relationships).

Controls are therefore more easily reused (for example, a Username control now just calls myMediator.UsernameEntered() rather than executing hard-coded logic fragments such as: "if password field is empty disable the OK button else enable the OK button").

It also promotes code reuse in the Mediator. For example, the Username and Password updates could be handled identically by the Mediator where previously we would have have the Username check the Password field (setting the OK button accordingly) and the Password field checking the Username field (setting the OK button accordingly). Now we just have the "UpdateOKButton" method once on the Mediator.

 

Momento

"Without violating encapsulation, capture and externalise an object's internal state so that the object can be restored to this state later"

Takes a snapshot of an object's state (all or partial depending on need) so that the state can, if necessary, be restored later and does so without exposing the objects implementation details.

Note:
The 'clever' bit about Memento is that it exposes a "wide" interface to the object being "memorized" but only a "narrow" interface to the object storing that memorization. In short, the client requesting the memorized object only sees that object through an interface that lets him copy it from one place to another.

 

Example:

None (the one in the GoF book is so poor as not to be worth repeating here).

Observer

"Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically"

AKA Publish - Subscribe (Publish = Subject, Subscribe = Observer)
AKA Events & Delegates in C#

Example:

Note:
With the "Pull" model (i.e. where we clone state) it's not unusual for subject & observer to be one class.

 

State

"Allow an object to alter its behaviour when its internal state changes. The object will appear to change it class"

Or: Get rid of repeated "if...else..." and "switch/case" statements.

Example:

The implementation of the Open() function for the class TCPIPSocket would vary depending on the object's state (i.e. whether its disconnected, listening, established etc.). We could code this as:

switch (state) {
    case disconnected:
        open_disconnected();
        break;

    case connected:
        throw new exception ConnectionAlreadyOpenException();
        break;
 

    case established:
        close();
        open(new);
        break;

    etc.
}

and write similar code for the close function, the sendData function, etc.

With the state pattern we use an associated state class to implement state-specific functionality:

<pic>

and the big switch statement just becomes:

this._state.Open();

(obviously we have to create/delete the object that _state points to as state changes)

 

 

Strategy

"Define a family of Algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it."

This pattern is particularly useful when dealing with algorithms of differing cost/benefits. For example, in a 3d modelling package we might have several rendering algorithms for a surface, each with its own time / quality issues. Say, WireFrame (fast but only edges are drawn), Solid (medium speed but unrealistic), and Ray-traced (very slow, realistic images).

 

Template Method

"Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure"

AKA, Abstract Methods!

void MyBaseClass::TemplateMethod() {
        <invariant bit>
        this.DoSomething1();        <---- Abstract method
        <invariant bit>
        this.DoSomething2();        <---- Abstract method
        ... etc...
}

Note:
The abstract methods are normally declared protected as well as virtual. This prevents clients calling them out of context.

 

Visitor

 

 

Notes From Pattern Hatching

Aggregation vs. Association: Aggregation when child should be deleted with parent.

p.25

Adaptor Lets you vary the interface to an object
Bridge Lets you vary the implementation of an object
Composite Lets you vary an object's structure and composition
Decorator Lets you vary the responsibilities without sub-classing
Facade Lets you vary the interface to a subsystem
Flyweight Lets you vary storage costs of an object
Proxy Lets you vary how an object is accessed and/or its location

p.32
Downcast = ?? casting from an interface or abstraction to a concrete class ??

p.40
Using a protected destructor to prevent clients deleting objects (a private destructor would prevent sub-classing)