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
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.
Pattern | Alias | Remarks |
---|
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.
As an example, think of providing viewers for alternative document formats,
such as VRML or PDF, in your application.
Using the State pattern, it suffices to
have a single viewer that changes itself according to the
format of the document viewed.
The Observer pattern
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?