CPSC 333: Design Patterns

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.


References

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

Introduction

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.''

Structure

Again, quoting, Gamma et al, ``a pattern has four essential elements:''

  1. 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.

  2. 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.

  3. 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.

  4. 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.

An Example

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.

Structure for ``Strategy''

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.

Structure for ``Strategy,'' Redrawn

The ``participants'' in the pattern are described as follows.

``Collaborations'' are described as follows.

Gamma, et al list several advantages and disadvantages of the use of this pattern (some, but not all, of which are listed here).

  1. Families of Related Algorithms Hierarchies of Strategy classes define a family of algorithms or behaviours for Contexts to reuse. Inheritance can help factor out common functionality of the algorithms.
  2. An Alternative to Subclassing. Inheritance offers another way to support a variety of algorithms or behaviours. You can subclass a Context class directly to give it different behaviours. But this hard-wires the behaviour into Context. It mixes the algorithm implementation with Context's, making Context harder to understand, maintain, and extend. And you can't vary the algorithm dynamically you wind up with many related classes whose only difference is the algorithm or behaviour they employ. Encapsulating the algorithm in separate Strategy classes lets you vary the algorithm independently of its Context, making it easier to switch, understand, and extend.
  3. Clients Must be Aware of Different Strategies. this pattern has a potential drawback in that a client must understand how Strategies differ before it can select the appropriate one. Clients might be exposed to implementation issues. Therefore you should use the Strategy pattern only when the variation in behaviour is relevant to clients.

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.

Mechanisms for Reuse, and Delegation

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.

How to Use a Pattern

Here is a ``step by step approach'' (again, given by Gamma, et al) to applying a pattern effectively.

  1. Read the pattern through once for an overview.
  2. Go back and study the (picture of the) structure, the descriptions of the roles that the pictured classes play, and the description of the collaborations between them.
  3. Look at the sample code provided with the pattern, to see a concrete example of the pattern in code.
  4. Define the classes you'll need - declare their interfaces, establish their inheritance relationships, and define the attributes that represent data and references to other objects. Identify existing classes in your system that the pattern will affect, and modify them accordingly.
  5. Define application-specific names for operations in the pattern. Use the described responsibilities and collaborations as a guide, and try to use naming conventions consistently
  6. Implement the operations to carry out the responsibilities and collaborations in the pattern.

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.

Additional Reference

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


Department of Computer Science
University of Calgary

Office: (403) 220-5073
Fax: (403) 284-4707

eberly@cpsc.ucalgary.ca