Introduction to Object Oriented Programming, 3rd Ed

Timothy A. Budd

Chapter 24

Design Patterns

Outline

  1. Roadmap
  2. Inspiration, and a Vocabulary
  3. Relationship to Dependency and Visibility
  4. A simple Example, the Adapter
    1. An Example Adapter
  5. Describing Patterns
  6. Example Patterns
    1. Iterator
    2. Software Factory
    3. Strategy
    4. Singleton
    5. Composite
    6. Decorator (Filter, Wrapper)
    7. Double Dispatch (Multiple Polymorphism)
      1. Double Dispatch, Continued
    8. Proxy
    9. Facade
    10. Observer
  7. Patterns and Frameworks

Other Material

Intro OOP, Chapter 24, Outline

Roadmap

When faced with a new problem, where do you look for inspiration?

Most people look to solutions of previous problems that have similar characteristics.

This insight is what is behind design patterns; collections of proven ways to structure the relatinships between objects in the pursuit of a given objective.

Intro OOP, Chapter 24, Slide 01

Inspiration, and a Vocabulary

Intro OOP, Chapter 24, Slide 02

Relationship to Dependency and Visibility

Many patterns are involved with the concepts of dependency we introduced in the previous chapter.

Determining where strong dependencies are necessary, and how to weaken dependencies whenver possible.

Intro OOP, Chapter 24, Slide 03

A Simple Example, The Adapter

An adaptor is used to connect a client (an object that needs a service) with a server (an object that provides the service).

The client requires a certain interface, and while the server provides the necessary functionality, it does not support the interface.

The adapter changes the interface, without actually doing the work.

Intro OOP, Chapter 24, Slide 04

The adapters in the Java graphics API are not true adapters in this sense of the word.

An Example Adapter

class MyCollection implements Collection {

	public boolean isEmpty () 
		{ return data.count() == 0; }
	public int size () 
		{ return data.count(); }
	public void addElement (Object newElement) 
		{data.add(newElement); }
	public boolean containsElement (Object test)
		{ return data.find(test) != null; }
	public Object findElement (Object test) 
		{ return data.find(test); }

	private DataBox data = new DataBox();
}
DataBox is some collection that does not support the Collection interface.
Adapters are often needed to connect software from different vendors.
Intro OOP, Chapter 24, Slide 05

Describing Patterns

Patterns themselves have developed their own vocabulary for description:
Intro OOP, Chapter 24, Slide 06

Example Patterns

We will briefly examine a number of common patterns:
Intro OOP, Chapter 24, Slide 07

Iterator

Problem: How do you provide a client access to elements in a collection, without exposing the structure of the collection.

Solution: Allow clients to manipulate an object that can return the current value and move to the next element in the collection.

Example, Enumerators in Java

interface Enumerator {
	public boolean hasMoreElements();
	public Object nextElement();
}

Enumeator e = ...;
while (e.hasMoreElements) {
	Object val = e.nextElement();
	...
}
The pattern applies, even if the interface is changed.
Intro OOP, Chapter 24, Slide 08

Software Factory

Problem: How do you simplify the manipulation of many different implementations of the same interface (i.e., iterators).

Solution: Hide creation within a method, have the method declare a return type that is more general than its actual return type.

class SortedList {
	...
	Enumerator elements () { return new SortedListEnumerator(); }
	...
	private class SortedListEnumerator implements Enumerator {
		...
	}
}
The method is the ``factory'' in the name. Users don't need to know the exact type the factory returns, only the declared type.

The factory could even return different types, depending upon circumstances.

Intro OOP, Chapter 24, Slide 09

Strategy

Problem: Allow the client the choice of many alternatives, but each is complex, and you don't want to include code for all.

Solution: Make many implementations of the same interface, and allow the client to select one and give it back to you.

Example: The layout managers in the AWT. Several different layout managers are implemented, and the designer selects and creates one.

Gives the designer flexibility, keeps the code size down.

Intro OOP, Chapter 24, Slide 10

Singleton

Problem: You want to ensure that there is never more than one instace of a given class.

Solution: Make the constructor private, have a method that returns just one instance, which is held inside the class itself.

class SingletonClass {
public:
	static SingletonClass * oneAndOnly () { return theOne; }
private:
	static SingletonClass * theOne;
	SingletonClass () { ... }
};

	// static initialization
SingletonClass * SingletonClass::theOne = new SingletonClass();
Intro OOP, Chapter 24, Slide 11

Composite

Problem: How do you facilitate creation of complex systems from simple parts?

Solution: Provide a few simple components, and a system to compose components (simple or otherwise) into new components.

Regular expressions are an example, are are type systems, or the nesting of panels within panels in the Java AWT API.

Intro OOP, Chapter 24, Slide 12

Decorator (Filter, Wrapper)

Problem: Allow functionally to be layered around an abstraction, but still dynamically changable.

Solution: Combine inheritance and composition. By making an object that both subclasses from anther class and holds an instance of the class, can add new behavior while referring all other behavior to the original class.

Example Input Streams in the Java I/O System

	// a buffered input stream is-an input stream
class BufferedInputStream extends InputStream {

	public BufferedInputStream (InputStream s) { data = s; }
	...

		// and a buffered input stream has-an input stream
	private InputStream data;
}
An instance of BufferedInputStream can wrap around any other type of InputStream, and simply adds a little bit new functionality.
Intro OOP, Chapter 24, Slide 13

Double Dispatch (Multiple Polymorphism)

Problem: You have variation in two or more polymorphic variables.

Solution: Make each a receiver in turn, each message ties down one source of variation.

Example, suppose we have a hierarchy of Shapes (Triangle, Square) and Device (Printer, Terminal). Two variables, one a Shape and one a Device.

First, pass a message to the device, passing the shape as argument:

Shape aShape = ... ;
Device aDevice = ...;

aDevice.display(aShape);

function Printer.display (Shape aShape)
begin
	aShape.displayOnPrinter (self);
end;

function Terminal.display (Shape aShape)
begin
	aShape.displayOnTerminal (self);
end;
One message fixes the device, but how to fix the shape?
Intro OOP, Chapter 24, Slide 14

Double Dispatch, continued

Each subclass of Shape must implement methods for each output device:
class Triangle : public Shape {
public:
	Triangle (Point, Point, Point);
		// ...
	virtual void displayOnPrinter (Printer);
	virtual void displayOnTerminal (Terminal);
		// ...
private:
	Point p1, p2, p3;
};

void Triangle.displayOnPrinter (Printer p) {
	// printer-specific code to 
	// display triangle
	// ...
}

void Triangle.displayOnTerminal (Terminal t) {
	// terminal-specific code to 
	// display triangle
	// ...
}
Intro OOP, Chapter 24, Slide 15

Proxy

Problem: How to hide unimportant communication details, such as a network, from the client.

Solution: A proxy uses the interface that the client expects, but passes messages over the network to the server, gets back the response, and passes it to the client. The client is therefore hidden from the network details.

Similar in some ways to adaptor, but here the intermediary and the server can have the same interface.

Intro OOP, Chapter 24, Slide 16

Facade

Problem: Actual work is performed by two or more objects, but you want to hide this level of complexity from the client.

Solution: Create a facade object that receives the messages, but passes commands on to the workers for completion.

Also similar to adapter and proxy.

Intro OOP, Chapter 24, Slide 17

Observer

Problem: How do you dynamically (at run time) add and remove connections between objects.

Solution: An Observer Manager implements the following protocol:

In this way neither the observer nor the observed object need know the existance of the other.

Intro OOP, Chapter 24, Slide 18

Patterns and Frameworks

Intro OOP, Chapter 24, Slide 19