Location: [CPSC 333] [Listing by Topic] [Listing by Date] [Previous Topic] [Next Topic] Design Patterns
This material was covered during lectures on March 19, 1997.
The primary reference for these notes is the following book, which includes a useful ``catalogue'' of patterns along with the material sketched here.
E. Gamma, R. Helm, R. Johnson, and J. Vlissides
Design Patterns: Elements of Reusable Object-Oriented Software
Addison-Wesley, 1995
Pree's book includes a survey of related work:
W. Pree,
Design Patterns for Object-Oriented Software Development
Addison-Wesley, 1995
Recent work on the identification of design patterns for the development has been motivated by the importance of ``patterns'' in crafting other types of complex systems. Indeed, a book which inspired much of this is
C. Alexander, S. Ishikawa, M. Silverstein, M. Jacobson, I. Fiksdahl-King, and S. Angel,
A Pattern Language
Oxford University Press, 1977
which investigates patterns in buildings and towns, rather than in computer systems. As defined in other systems,
``Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without doing the same twice.'' (A Pattern Language, Page x)
Gamma, et al, define a design pattern (in object-oriented design) to be something that ``names, abstracts, and identifies the key aspects of a common design structure that make it useful for creating a reusable object-oriented design.'' (Design Patterns, Page 3).
Gamma, et al, go on to say that a design pattern ``identifies the participating classes and instances, their roles and collaborations, and the distribution of responsibilities. Each diagram focuses on a particular object-oriented design problem or issue. It describes when it applies, whether it can be applied in view of other design constraints, and the consequences and trade-offs of its use. Since we must eventually implement our design, a design pattern also provides sample C++ and (sometimes) Smalltalk code to illustrate an implementation.''
Again, quoting, Gamma et al, ``a pattern has four essential elements:''
The pattern name is a handle that can be used to describe a design problem, its solutions, and consequences in a few words. Naming a pattern increases our design vocabulary, and having a vocabulary for patterns lets us talk about them to our colleagues and in our documentation.
The problem describes when to apply the pattern. It explains the problem and its context. Sometimes the problem will include a list of conditions that must be met before it makes sense to apply the pattern.
The solution describes the elements that make up the design, their relationships, responsibilities, and collaborations. It doesn't describe a particular concrete design or implementation, because a pattern is like a ``template'' that can be applied in many different situations. Instead, it gives an abstract description of a design problems and how a general arrangement of elements can solve it.
The consequences are the results and trade-offs of applying the pattern. Consequences often concern time and space trade-offs, but may also address language and implementation issues, or the impact of use of the pattern on the system's flexibility, extensibility, or portability.
One pattern given by Gamma, et al, has the name Strategy. It is to be used when
A picture describing the ``structure'' of a solution (shown on page 316 of Design Patterns) is as follows.
Gamma, et al's graphical notation is rather different from the notation that's been used so far in this course. A redrawn picture using something closer to Coad and Yourdon's notation would look as follows.
The ``participants'' in the pattern are described as follows.
Strategy declares an interface common to all supported algorithms. ``Context'' uses this interface to call the algorithm defined by a ``Concrete Strategy.''
Concrete Strategy implements the algorithm using the ``Strategy'' interface.
Context is configured with a ``Concrete Strategy'' object. It also maintains a reference to a ``Strategy'' object, and may define an interface that lets ``Strategy'' access its data.
``Collaborations'' are described as follows.
``Strategy'' and ``Context'' interact to implement the chosen algorithm. A context may pass all data required by the algorithm to the Strategy when the algorithm is called. Alternatively, the Context can pass itself as an argument to Strategy operations. That lets the Strategy call back on the Context as required.
A Context forwards requests from its clients to its Strategy. Clients usually create and pass a ``Concrete Strategy'' object to the Context; thereafter, clients interact with the Context exclusively. There is often a family of ``Concrete Strategy'' classes for a client to choose from.
Gamma, et al list several advantages and disadvantages of the use of this pattern (some, but not all, of which are listed here).
Additional potential drawbacks include communication overhead between Strategy and Context, and the use of an increase number of objects.
Gamma, et al also discuss additional implementation issues, provide sample code, cite known uses of the pattern, and list ``related patterns'' that can be used in combination with this one.
Gamma, et al also discuss design guidelines that are reflected in the patterns they give. One important idea is that you should always ``program to an interface, not an implementation'' - that is, try only to use the interface provided by an abstract class whenever you can, instead of taking advantage of an implementation that one particular ``concrete'' class (that's a realization of the abstract class) might provide.
Gamma, et al, also recommend that you try to use Composition (whole-part structures) instead of Inheritance (generalization-specialization structures) when combining classes in order to improve functionality. While inheritance is often easier to use, and can lead to systems that are easy to understand, it tends to be a form of ``white box'' reuse, in that a specialization can depend on the implementation of a generalization - and can be damaged when the generalization class is modified. Composition is more a form of ``black box'' reuse, in the sense that classes must communicate through their interfaces when combined in whole-part structure - making the systems you build from them more resistant to change.
Delegation is a way of making object composition as powerful for reuse as inheritance is. In delegation, two objects are involved in handling a requests: a class receiving a request delegates operations to its delegate. It does so by ``passing itself'' to the delegate, to let the delegated operation refer to the receiver.
For example, in a graphical user interface system, you might have two classes that seem similar - a Window class, and a Rectangle class, and it might be helpful for the Window class to use some of the Rectangle class's operations in order to fulfill its own responsibilities.
One way to combine the classes together would be to use a generalization-structure, essentially declaring that a Window is a ``kind of'' an Rectangle. Using ``delegation,'' you would create a whole-part structure (with the Window as the ``whole'' and the Rectangle as the ``part,'') declaring that the Window has a Rectangle, instead. Now, the Window will be a class that ``receives'' several messages, that it can handle by using its Rectangle as a ``delegate.''
According to Gamma, et al, the main advantage of delegation over inheritance is that it makes it easy to compose behaviours at run-time and to change the way they're composed. For example, it would be easy to make the Window circular (instead of rectangular) at run-time, simply by replacing its Rectangle delegate with a Circle delegate.
Disadvantages of delegation include the fact that it leads to dynamic, highly parameterized systems that are harder to understand than the more static systems that can be created using inheritance instead. ``Delegation is a good design choice only when it simplifies more than it complicates.
Several of the patterns given by Gamma, et al - including Strategy - use delegation. In particular, in ``Strategy,'' the ``Context'' class uses ``Strategy'' as a delegate.
Here is a ``step by step approach'' (again, given by Gamma, et al) to applying a pattern effectively.
Gamma, et al also give advice about how to choose a design pattern, and describe additional design and implementation issues. A case study showing the use of design patterns to design a ``Document Editor'' is also included.
Rob Kremer has produced a tutorial on design patterns, which you should be able to view using Netscape (or another equally powerful brower). This includes a picture of the ``structure'' of each of the design patterns given by Gamma, et al, as well as a bit of information about the applicability of these patterns, and consequences of their use.
Location: [CPSC 333] [Listing by Topic] [Listing by Date] [Previous Topic] [Next Topic] Design Patterns