CSC207 Software Design
Lectures
Design Patterns

Nothing New Under the Sun

A design pattern is a way of doing things

Porches

Nested loops

Too fuzzy to put in a library

But a great way to talk about design issues

The architect Christopher Alexander invented the term

Architectural styles can be described as pattern languages

Because certain patterns usually occur together

The "Gang of Four" book (1994)

Applied the notion to object-oriented programming

Named many widely-used patterns

Today

Dozens of books, conferences, etc.

Terms are widely (if inaccurately) used

Format

Pattern name

Synopsis: a short summary of the pattern

Context: what problem does the pattern address?

Often presented as an example

Forces: what factors influence the decision about whether or not to use this pattern?

Solution: what is the general-purpose solution to the problem?

Consequences: what are the results (good and bad) of using this pattern?

Example

Related Patterns

Abstract Factory

Synopsis: create instances of related classes

Context: want to separate main application from details of specific framework classes

E.g. separate parser from objects being created

Or build GUI to run on Windows, Unix, and cellphones

Forces:

A system that works with multiple products should not contain product-specific code

It should not be possible to create component A from family X, but component B from family Y

Creation mechanism should be extensible, so that it can support new platforms in future

Solution:

Define one interface for each type of component

Create a an abstract factory class with one creation method for each interface

Derive one concrete factory class for each platform, filling in the creation methods

Example:

Graph parser that can produce graph nodes of different (user-defined) types

Abstract factory that created different objects for testing assignment

Prototype

Synopsis: create objects by cloning, without knowing exactly what is being cloned

Context: want to separate main application from details of specific classes being manipulated

E.g. building a WYSIWYG editor for web pages

Or creating instructions for a virtual machine

Forces:

A system should be able to create objects without knowing their exact class

Creation mechanism should be extensible, so that it can handle new objects in future

Solution:

Define an interface that all prototype objects must implement

Give applications a way to register prototypes

When a new object is needed, clone the appropriate prototype

Example:

Graph parser that can produce graph nodes of different (user-defined) types

Allow applications to extend a virtual machine's instruction set by registering new instructions

Wait A Second...

Abstract Factory and Prototype sound very similar

Both create objects without knowing exactly what they are

Both can be extended by users after deployment

How do you tell which one to use when?

Look at what else these classes need to do

Be consistent with existing code

Build up personal experience

Singleton

Synopsis: ensure that only one instance of a class is created

Context: want every object that uses a class to use the same instance of that class

E.g. the graphics driver for the screen

Forces:

There must be exactly one instance of the class

All objects that use the class must have access to that instance

Solution:

Make the constructor private so that other classes can't get at it

Provide a static method for construction

Have that method keep a copy of the first object it creates, and re-use it on subsequent calls

Example:

class GraphicsDriver {
    private GraphicsDriver() {
        ...construct graphics driver...
    }

    public static GraphicsDriver getInstance() {
        if (sInstance == null) {
            sInstance = new GraphicsDriver();
        }
        return sInstance;
    }

    private static GraphicsDriver sInstance;
}

class Application {
    public static void main(String[] args) {
        GraphicsDriver gd = GraphicsDriver.getInstance();
        gd.clearScreen();
        doWork();
    }

    public static void doWork() {
        while (working) {
            GraphicsDriver gd = GraphicsDriver.getInstance();
            ...draw things...
            gd.display();
        }
    }
}

Singleton Commentary

Why not make the instance member variable public, and access it directly?

Because some day you might want one per thread, or one per monitor in a multi-headed display, or...

Code evolves

Design patterns reflect experts' experience with managing that evolution

Why not create the instance variable in a static block?

Might not have necessary parameters

Can't control order in which static initialization blocks are executed

Filter

Synopsis: dynamically connect objects to transform a data stream

Context: some computations most easily expressed as a sequence of transformations

E.g. image processing

Unix commands joined by pipes

Also allows uses to mix and match operations easily

Forces:

Every filter should read and write the same format, so that they can be chained together

It should be possible to chain filters together in arbitrary orders

Solution:

Have each filter implement an interface that reads and writes data

Define special cases for sources (that only write) and sinks (that only read)

Provide filters with a way to signal that they have nothing more to produce

Example:

The multi-filter example from earlier in the course

The standard Unix command line tools

Composite

Synopsis: build complex hierarchies by making sub-hierarchies look like individual objects

Context: want to be able to treat nested collections uniformly

E.g. combine tests and test suites

Or create documents that contain tables containing text containing tables

Forces:

Want other classes to be able to treat collections and individual objects uniformly

Want to minimize the number of special cases that collections have to be aware of

Solution:

Create an interface (or abstract class) defining the properties of both individuals and collections

Have individuals and collections implement this interface

Have collections treat everything they contain as instances of this interface

Example:

In JUnit, TestCase and TestSuite both implement Test

TestSuite contains zero or more Tests

So it can contain both test cases and test suites

Regions in spreadsheets

Individual cells return their own values

Larger regions return the sum, average, etc.

Visitor

Synopsis: separate traversal of a complex structure from operations on that structure

Context: want an easy way to do walk around a complex structure

E.g. visit each node in a graph once

Forces:

Many different operations may need to be performed

Structure is complex enough that visiting elements is error-prone

The types of objects in the structure, and the ways they are connected, change infrequently

So it's worth investing time in building a support tool

Solution #1: create an iterator class with do-nothing methods that are called at specific times

This class takes care of things like remembering which nodes have already been seen

Users derive their own iterators from this, filling in methods to do what they want

Solution #2: create one iterator class that takes a callback argument

Callback only provides methods that do things

Iterator calls these at appropriate times

Solution #1 is simpler, but:

Some languages (e.g. C) don't support derivation, so you have to use solution #2

Solution #2 allows you to re-use callbacks with different kinds of collections

Starts to look like an inside-out Filter

Example:

Graph visitor

Persistence

Adapter

Synopsis: implement an interface known to one set of classes so that they can communicate with other objects they don't know about

Context: want to use a class in a way that its original author didn't anticipate

E.g. write data to a string instead of to a file

Or apply regular expressions to streams instead of to strings

Forces:

You want to use a class as though it implemented an interface that it doesn't actually implement

You do not want to modify or extend that class

You can translate the operations you want to perform to the ones the class actually implements

Solution: create an adapter that implements the interface you want, and calls the methods the class has

Example: both Python and Java provide "string I/O" classes

def testStream(input, output):
    lineIn = input.readline()
    while lineIn:
        lineOut = someTest(lineIn)
        output.write(lineOut)
        lineIn = input.readline()

# test with real streams --- relies on external files
# and 'diff' to check results
for arg in sys.argv[1:]:
    input = open(arg, 'r')
    testStream(input, sys.stdout)
    input.close()

# better: test with canned input and output (self-contained)
sampleInput  = "a\nb\nc\n"
sampleOutput = "AA\nBB\nCC\n"
input = StringIO(sampleInput)
output = StringIO()
testStream(input, output)
assert output.getvalue() == sampleOutput

Other Favorites

Cache: store temporary copies of objects locally to improve performance

Command: represent actions as objects

Give them "combine" and "undo" methods

This is how editors work

State: record state of program as object so that it can be re-started

Null Object: use an object that does nothing in place of null

Saves testing that object isn't null before doing operations

Proxy: use one object as an interface to another

Differs from Adapter in that the proxy object has the same interface as what it's proxying for

Typically, the proxy is local, and the real object is on another machine

And Many, Many More

Lots of books, on-line collections, conferences, etc.

Quality is uneven

But that's true of everything

Not just about object-oriented design

User interface patterns

Business patterns

Anti-patterns (things to avoid)

In practice, serve two purposes:

Communication: a concise way for designers to communicate with each other

And argue out exactly what they mean

Education: gives them a way to communicate what they know to newcomers

Like you

If you buy one book for this course, buy a book on design patterns

Don't expect to connect them all to your own experience the first time

But keep them in mind as you work on other courses

"Hey, I know how to do this!"


$Id: patterns.html,v 1.1.1.1 2004/01/04 05:02:31 reid Exp $