You are on page 1of 81

DESIGN PATTERN OVERVIEW AND EXAMPLE CODE

Motivation and Concept


OO systems exploit recurring design structures that promote
E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Abstraction Flexibility Modularity Elegance

Therein lies valuable design knowledge Problem: capturing, communicating, and applying this knowledge

What Is a Design Pattern?


A design pattern
Is a common solution to a recurring problem in design Abstracts a recurring design structure Comprises class and/or object
Dependencies Structures Interactions Conventions

E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Names & specifies the design structure explicitly Distils design experience

What Is a Design Pattern?


A design pattern has 4 basic parts:
1. Name 2. Problem 3. Solution 4. Consequences and trade-offs of application

E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Language- and implementation-independent A micro-architecture Adjunct to existing methodologies (Unified, OMT, etc.) No mechanical application
The solution needs to be translated into concrete terms in the application context by the developer

Goals
Codify good design
Distil and disseminate experience Aid to novices and experts alike Abstract how to think about design
E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Give design structures explicit names


Common vocabulary Reduced complexity Greater expressiveness

Capture and preserve design information


Articulate design decisions succinctly Improve documentation

Facilitate restructuring/refactoring
Patterns are interrelated Additional flexibility

Design Pattern Catalogues


GoF (the gang of four) catalogue
Design Patterns: Elements of Reusable Object-Oriented Software, Gamma, Helm, Johnson, Vlissides, Addison-Wesley, 1995

POSA catalogue
Pattern-Oriented Software Architecture, Buschmann, et al.; Wiley, 1996

Classification of GoF Design Pattern


Creational Factory Method Abstract Factory Builder Prototype Singleton Structural Adapter Bridge Composite Decorator Flyweight Facade Proxy Behavioral Interpreter Template Method Chain of Responsibility Command Iterator Mediator Memento Observer State Strategy Visitor

E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Observer (Behavioral)
Intent
Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically
E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Applicability
When an abstraction has two aspects, one dependent on the other When a change to one object requires changing others, and you don't know how many objects need to be changed When an object should notify other objects without making assumptions about who these objects are

Observer (Cont'd)
Structure
Subject
E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

-observers 1 {for all o in observers { o.update() } } *

Observer +update()

+attach(in o : Observer) +detach(in o : Observer) +notify()

ConcreteSubject -subjectState +getState()

-subject 1 {return subjectState } *

ConcreteObserver -observerState +update() {observerState = subject.getState()

Observer (Cont'd)
Consequences
+ Modularity: subject and observers may vary independently + Extensibility: can define and add any number of observers + Customizability: different observers provide different views of subject Unexpected updates: observers don't know about each other Update overhead: might need hints

E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Implementation
Subject-observer mapping Dangling references Avoiding observer-specific update protocols: the push and push/pull models Registering modifications of interest explicitly

Observer (Cont'd)
Known uses
Smalltalk model-view-controller (MVC) Interviews (subjects and views) Andrew (data objects and views)

E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Benefits
Design reuse Uniform design vocabulary Enhance understanding, restructuring Basis for automation

Observer (Cont'd)
Structure
Subject
E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

-observers 1 {for all o in observers { o.update() } } *

Observer +update()

+attach(in o : Observer) +detach(in o : Observer) +notify()

ConcreteSubject -subjectState +getState()

-subject 1 {return subjectState } *

ConcreteObserver -observerState +update() {observerState = subject.getState()

Schematic Observer Example


Observers

Subject

Observer - Sample Code


class Subject { public: virtual ~Subject(); virtual void Attach(Observer*); virtual void Detach(Observer*); virtual void Notify(); protected: Subject(); private: List<Observer*> *_observers; }; void Subject::Attach (Observer* o) { _observers->Insert(_observers->end(), o); } void Subject::Detach (Observer* o) { _observers->remove(o); } void Subject::Notify () { ListIterator<Observer*>i(_observers); for (i.First(); !i.IsDone(); i.Next()) { i.CurrentItem()->Update(this); } } class Observer { public: virtual ~Observer(); virtual void Update(Subject* theChangeSubject) = 0; protected: Observer(); }; class ClockTimer : public Subject { public: ClockTimer(); virtual int GetHour(); virtual int GetMinute(); virtual int GetSecond(); void Tick(); }; void ClockTimer::Tick() { // update internal time-keeping state // ... Notify(); }

Observer Sample Code


class DigitalClock: public Observer { public: DigitalClock(ClockTimer *); ~DigitalClock(); void Update(Subject *); void Draw(); private: ClockTimer *_subject; }; DigitalClock::DigitalClock (ClockTimer *s) { _subject = s; _subject->Attach(this); } DigitalClock::~DigitalClock () { _subject->Detach(this); } void DigitalClock::Update (Subject *theChangedSubject) { if(theChangedSubject == _subject) draw(); } void DigitalClock::Draw () { int hour = _subject->GetHour(); int minute = _subject->GetMinute(); int second = _subject->GetSecond(); // draw operation class AnalogClock: public Observer { public: AnalogClock(ClockTimer *); ~AnalogClock(); void Update(Subject *); void Draw(); private: ClockTimer *_subject; }; int main(void) { ClockTimer *timer = new ClockTimer; AnalogClock *analogClock = new AnalogClock(timer); DigitalClock *digitalClock = new DigitalClock(timer); timer->Tick(); return 0; }

Overview
Motivation and Concept
Observer

Example: WYSIWYG Editor


Composite Strategy Decorator Abstract Factory

Composite (Structural)
Intent
Treat individual objects and multiple, recursivelycomposed objects uniformly
E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Applicability
Objects must be composed recursively, And there should be no distinction between individual and composed elements, And objects in the structure can be treated uniformly Part-Whole hierarchy of objects. Constant handling of objects as groups or individuals

Composite (Cont'd)
Structure
Component * +operation() +add(in c : Component) +remove(in c : Component) +getChild(in i : int) -children

E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Leaf +operation()

Composite +operation() +add(in c : Composite) +remove(in c : Composite) +getChild(in i : int)

{ forall g in children g.operation(); }

Composite (Cont'd)
Consequences
+ Uniformity: treat components the same regardless of complexity + Extensibility: new Component subclasses work wherever old ones do Overhead: might need prohibitive numbers of objects

E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Implementation
Do Components know their parents? Uniform interface for both leaves and composites? Don't allocate storage for children in Component base class Responsibility for deleting children

Composite (Cont'd)
Known Uses
ET++ VObjects InterViews Glyphs, Styles Unidraw Components, MacroCommands

E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Composite - Example

Composite Sample Code


Currency CompositeEquipment::NetPrice() { Iterator<Equipment*>* i = getIterator(); Currency total = 0; for (i->First(); !i->IsDone(); i->Next()) total += i->CurrentItem()->NetPrice(); delete i; return total; } // and in the client code (e.g. in main) Cabinet* cabinet = new Cabinet("PC Cabinet"); Chassis* chassis = new Chassis("PC Chassis"); cabinet->Add( chassis ); Bus* bus = new Bus ("MCA Bus"); bus ->Add( new Card("16Mbs Token Ring") ); chassis->Add( bus ); chassis->Add( new FloppyDisk("3.5 floppy") ); cout << chassis->NetPrice() << endl;

Strategy (Behavioral)
Intent
Define a family of algorithms, encapsulate each one, and make them interchangeable to let clients and algorithms vary independently

E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Applicability
When an object should be configurable with one of several algorithms, and all algorithms can be encapsulated, and one interface covers all encapsulations

Strategy (Cont'd)
Structure

E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Context (Composition) +contextInterface() 1 1

Strategy (Compositor) +algorithmInterface()

ConcreteStrategyA +algorithmInterface()

ConcreteStrategyB +algorithmInterface()

ConcreteStrategyC +algorithmInterface()

Strategy (Cont'd)
Consequences
+ + Greater flexibility, reuse Can change algorithms dynamically Strategy creation & communication overhead Inflexible strategy interface

E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Implementation
Exchanging information between a strategy and its context Static strategy selection via templates

Known uses
Interviews text formatting RTL register allocation & scheduling strategies Et++SwapsManager calculation engines

Strategy - Example

Strategy Sample Code


class ConcreteContext : public Context{ public: ConcreteContext(); ConcreteContext(Strategy<class T> *); ConcreteContext(const ConcreteContext &); int array[10]; Strategy *thisStratergy; Boolean aggregation; void execute(); void attachStrategy(Strategy *s); }; template <class T> class DecreasePrint : public Strategy<T>{ public: virtual void doAlgorithm(T* const cont); void quicksortD(int array[], int l, int r); DecreasePrint<class T> *clone(); int array[10]; }; template <class T> void IncreasePrint<T>::doAlgorithm( T* const cont) { for (int i=0; i<10; i++) array[i] = cont->array[i]; quicksortI(array, 0, 9); printf("INCREASING ORDER\n"); for (int i=0; i<10; i++) ::printf("Element no %d = %d\n", i, array[i]); }

template <class T> class Print : public Strategy<T>{ public: virtual void doAlgorithm(T* const); Print<class T> *clone(); };

Strategy Sample Code


void Context::execute(){ template <class T> void DecreasePrint<T>::doAlgorithm(T* const cont){ if(thisStrategy){ thisStrategy->doAlgorithm((T *)this); for (int i=0; i<10; i++) } array[i] = cont->array[i]; else quicksortD(array, 0, 9); { ::printf("Error: there is no strategy attached to the context\n"); ::printf("DECREASING ORDER\n"); ::printf("An exeception has been thrown\n"); for (int i=0; i<10; i++) throw "Error: there is no stategy attach to the context"; ::printf("Element no %d = %d\n", i, array[i]); } } } void Context::attachStrategy(Strategy<class T> * anotherStrategy){ if (aggregation) delete thisStrategy; thisStrategy = anotherStrategy; }

Strategy - Sample Code


Context::Context(const Context &t) { if(t.aggregation){ thisStrategy = t.thisStrategy->clone(); aggregation = true; } else { thisStrategy = t.thisStrategy; aggregation = false; } }

Strategy Sample Code


main(int argc, char **argv){ ConcreteContext * context1 = new ConcreteContext(); context1->attachStrategy((Strategy<T> *)(new Print<ConcreteContext>())); context1->execute(); ::printf("*****************\n"); ConcreteContext * context2 = new ConcreteContext(*context1); // Copy constructor context1->attachStrategy((Strategy<T> *)(new IncreasePrint<ConcreteContext>())); context1->execute(); ::printf("*****************\n"); context1->removeStrategy(); context1->attachStrategy((Strategy<T> *)(new DecreasePrint<ConcreteContext>())); context1->execute(); context1->removeStrategy(); delete context1; ::printf("*****Context2******\n"); context2->execute(); context2->removeStrategy(); delete context2; :: printf("\n Testing Aggregation \n\n"); context1 = new ConcreteContext( (Strategy<T> *)(new IncreasePrint<ConcreteContext>())); context1->execute(); ::printf("*****************\n"); context2 = new ConcreteContext(*context1); delete context1; context2->execute(); delete context2; }

Decorator (Structural)
Intent
Augment objects with new responsibilities
E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Applicability
When extension by subclassing is impractical For responsibilities that can be withdrawn

Decorator Diagram
Structure
Component (Glyph)
E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

1 -component

+operation()

ConcreteComponent +operation()

Decorator (MonoGlyph) { component-> component.operation(); } +operation() 1

ConcreteDecoratorA -addedState +operation()

ConcreteDecoratorB +operation() +addedBehavior()

{ super.operation(); addedBehavior(); }

Decorator - Diagram

Decorator Overview
A Decorator, also known as a Wrapper, is an object that has an interface identical to an object that it contains. Any calls that the decorator gets, it relays to the object that it contains, and adds its own functionality along the way, either before or after the call. Therefore, the Decorator Pattern is used for adding additional functionality to a particular object as opposed to a class of objects. It is easy to add functionality to an entire class of objects by subclassing an object, but it is impossible to extend a single object this way. With the Decorator Pattern, you can add functionality to a single object and leave others like it unmodified.

Decorator Comments
The Decorator pattern gives you a lot of flexibility, since you can change what the decorator does at runtime, as opposed to having the change be static and determined at compile time by subclassing. Since a Decorator complies with the interface that the object that it contains, the Decorator is indistinguishable from the object that it contains. That is, a Decorator is a concrete instance of the abstract class, and thus is indistinguishable from any other concrete instance, including other decorators. This can be used to great advantage, as you can recursively nest decorators without any other objects being able to tell the difference, allowing a near infinite amount of customization.

Decorator (Cont'd)
Consequences
+ + + Responsibilities can be added/removed at run-time Avoids subclass explosion Recursive nesting allows multiple responsibilities Interface occlusion Identity crisis

E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Implementation
Interface conformance Use a lightweight, abstract base class for Decorator Heavyweight base classes make Strategy more attractive

Decorator (Cont'd)
Known Uses
Embellishment objects from most OO-GUI toolkits ParcPlace PassivityWrapper InterViews DebuggingGlyph

E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Decorator Example

Decorator Example
class Book extends LibItem{ String author; intnoCopies; intonShelf; String title; public Book(String t, String a, int c){ title = t; noCopies= c; author = a; onShelf= c; } public String getTitle() { return title; } public int getCopies() { return noCopies; } public int copiesOnShelf() { return onShelf; } public void decCopiesOnShelf() { onShelf--; } public void incCopiesOnShelf() { onShelf++; } public String getAuthor() { return author; } public void borrowItem(String borrower) { System.out.println("borrowin Book"); } public void returnItem(String borrower) { } public void reserve(String reserver) { } } /* End class Book */

Decorator Example
abstract class Decorator extends LibItem{ LibItem item; public Decorator(LibItem li) { item = li; } public String getTitle() { return item.getTitle(); } // Following methods are // similarly implemented public String getAuthor() {... public intgetCopies() { ... public intcopiesOnShelf() { ... public void decCopiesOnShelf() { item.decCopiesOnShelf(); } // and similarly... public void incCopiesOnShelf() {...

Decorator Example

Decorator Example - (client)


// Non borrowable, non reservablebook LibItem b1 = new Book("A", "B", 1); // Borrowablevideo LibItem v1 = new BorrowableDec(new Video("V", 3)); // borrow unborrowableitem -copies should stay 1 b1.borrowItem("Bob"); System.out.println("Copiesof book = " +b1.copiesOnShelf()); // borrow video -copies decremented to 2 v1.borrowItem("Fred"); System.out.println("Copiesof video = " +v1.copiesOnShelf()); //make book borrowableand borrow it -copies = 0 LibItem b2 = new BorrowableDec(b1); b2.borrowItem("Bob"); System.out.println("Copiesof book = " +b2.copiesOnShelf()); // make book reservable LibItem b3 = new ReservableDec(b2); b3.reserve("Alice"); b3.returnItem("Bob"); // book returned -back to 1 copy System.out.println("Copiesof book = " +b3.copiesOnShelf()); // Not reserved for Jane -still 1 copy b3.borrowItem("Jane"); System.out.println("Copiesof book = " +b3.copiesOnShelf()); // Okay Alice can borrow -down to 0 b3.borrowItem("Alice"); System.out.println("Copiesof book = " +b3.copiesOnShelf());

Abstract Factory (Creational)


Intent
Create families of related objects without specifying class names
E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Applicability
When clients cannot anticipate groups of classes to instantiate

Abstract Factory (Cont'd)


Structure
AbstractFactory +createProductA() +createProductB() Client * * * * * AbstractProductA

E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

instantiate
ConcreteFactory1 +createProductA() +createProductB() ConcreteFactory2 +createProductA() +createProductB()

ProductA1

ProductA2

instantiate

AbstractProductB

instantiate

instantiate
ProductB1 ProductB2

Abstract Factory - Example


public class MazeFactory { public MazeFactory() {...} public Maze makeMaze(){ return new Maze(); } public Room makeRoom(int n) { return new Room(n); } public Wall makeWall() { return new Wall(); } public Door makeDoor(Room r1, Room r2) { return new Door(r1, r2); } }; public class MazeGame { // ... public Maze createMaze (MazeFactory factory) { Maze aMaze = factory.makeMaze(); Room r1 = factory.makeRoom(1); Room r2 = factory.makeRoom(2); Door theDoor = factory.makeDoor(r1, r2); aMaze.addRoom(r1); aMaze.addRoom(r2); r1.setSide(MapSite.NORTH, factory.makeWall()); r1.setSide(MapSite.EAST, theDoor); r1.setSide(MapSite.SOUTH, factory.makeWall()); r1.setSide(MapSite.WEST, factory.makeWall()); r2.setSide(MapSite.NORTH, factory.makeWall()); r2.setSide(MapSite.EAST, factory.makeWall()); r2.setSide(MapSite.SOUTH, factory.makeWall(); r2.setSide(MapSite.WEST, theDoor); return aMaze; } }

Abstract Factory - Example


public class EnchantedMazeFactory extends MazeFactory { public EnchantedMazeFactory() {...} public Room makeRoom(int n) { return new EnchantedRoom(n, new Spell()); } public Door makeDoor(Room r1, Room r2) { return new DoorNeedingSpell(r1, r2); } } public class BombedMazeFactory extends MazeFactory { public BombedMazeFactory() {...} public Wall makeWall(){ return new BombedWall(); } public Room makeRoom(int n){ return new RoomWithABomb(n); } }

Abstract Factory - Client


MazeGame game; // The instance of a game. . Maze aMaze; // A reference to a maze. switch(choice) { case ENCHANTED: { EnchantedMazeFactory factory = new EnchantedMazeFactory(); aMaze = game.createMaze(factory); break; } case BOMBED: { BombedMazeFactory factory = new BombedMazeFactory(); aMaze = game.createMaze(factory); break; } }

Abstract Factory (Cont'd)


Consequences
+ Flexibility: removes type dependencies from clients + Abstraction: hides product's composition Hard to extend factory interface to create new products

E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Implementation
Parameterization as a way of controlling interface size Configuration with Prototypes

Known Uses
InterViews Kits ET++ WindowSystem

Overview
Motivation and Concept
Observer

Example: WYSIWYG Editor


Composite - Document Structure Strategy - Formatting Decorator - Embellishment Abstract Factory - Multiple Look&Feels

Iterator (Behavioral)
Intent
Access elements of an aggregate sequentially without exposing its representation
E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Applicability
Require multiple traversal algorithms over an aggregate Require a uniform traversal interface over different aggregates When aggregate classes and traversal algorithm must vary independently

Iterator (cont'd)
Structure
Iterator Aggregate (Glyph)
E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Client * * * * +first() +next() +isDone() +currentItem()

+createIterator()

ConcreteAgregate +createIterator() 1 *

ConcreteIterator

{ return ConcreteIterator(this); }

Iterator Examle
import java.util.*; class IntSet { private Hashtable ht = new Hashtable(); public static class Iterator { private IntSet set; private Enumeration e; private Integer current; public Iterator( IntSet in ) { set = in; } public void first() { e = set.ht.keys(); next(); } public boolean isDone() { return current == null; } public int currentItem() { return current.intValue(); } public void next() { try { current = (Integer) e.nextElement(); } catch (NoSuchElementException e) { current = null; } } } public void add( int in ) { ht.put( new Integer( in ), "null" ); } public boolean isMember( int i ) { return ht.containsKey(new Integer(i)); } public Hashtable getHashtable() { return ht; } public Iterator createIterator() { return new Iterator( this ); } }

Iterator Example
class IteratorDemo { public static void main( String[] args ) { IntSet set = new IntSet(); . // Code to add elements to the set and other code // Clients ask the collection object to create many iterator objects IntSet.Iterator it1 = set.createIterator(); IntSet.Iterator it2 = set.createIterator(); // Clients use the first(), isDone(), next(), currentItem() protocol System.out.print( "\nIterator: " ); for ( it1.first(), it2.first(); ! it1.isDone(); it1.next(), it2.next() ) System.out.print( it1.currentItem() + " " + it2.currentItem() + " " ); System.out.print( "\nEnumeration: " ); for (Enumeration e = set.getHashtable().keys(); e.hasMoreElements(); ) System.out.print( e.nextElement() + " " ); System.out.println(); } }

Iterator (cont'd)
Consequences
+ Flexibility: aggregate and traversal are independent + Multiple iterators multiple traversal algorithms Additional communication overhead between iterator and aggregate

E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Implementation
Internal versus external iterators Violating the object structure's encapsulation Robust iterators

Known Uses
Penpoint traversal driver/slave InterViews ListItr Unidraw Iterator

Overview
Example: WYSIWYG Editor (Cont'd)
Spelling checking & hyphenation
Iterator Visitor

Some more patterns


Template Method Singleton Faade

Observations and Conclusions Further reading

Visitor (Behavioral)
Intent
Centralize operations on an object structure so that they can vary independently but still behave polymorphically
E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Applicability
When classes define many unrelated operations Class relationships of objects in the structure rarely change, but the operations on them change often Algorithms over the structure maintain state that's updated during traversal

Visitor (cont'd)
Structure
Client * * * Visitor +VisitConcreteElement1(ConcreteElement1)() +VisitConcreteElement2(ConcreteElement2)() * ObjectStructure * 1 Element +accept(in v : Visitor)

E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

ConcreteElement1 +accept(in v : Visitor)

ConcreteElement2 +accept(in v : Visitor)

ConcreteVisitor +VisitConcreteElement1(ConcreteElement1)() +VisitConcreteElement2(ConcreteElement2)() { v.visitConcreteElement1(this); } { v.visitConcreteElement2(this); }

Visitor (cont'd)
Consequences
+ + Flexibility: visitor and object structure are independent Localized functionality Circular dependency between Visitor and Element interfaces Visitor brittle to new ConcreteElement classes Double dispatch Overloading visit operations Catch-all operation General interface to elements of object structure

E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Implementation

Known Uses
ProgramNodeEnumerator in Smalltalk-80 compiler IRIS Inventor scene rendering

ExampleA Printing Visitor


public class SomeContainer implements Container { public void accept (Visitor visitor) { for each Object i in this container{ visitor.visit (i); if (visitor.isDone(this)) break; Definition of SomeContainer } } and of the accept method // ... } public class PrintingVisitor extends AbstractVisitor { public void visit (Object object) { System.out.println (object); } // ... Use of visitor } // // in another class... Container c = new SomeContainer (); // ... c.accept (new PrintingVisitor ());

Editor Example Summary


Document Structure
Composite

Formatting
Strategy

Embellishment
Decorator

Multiple Look&Feels
Abstract Factory

(Multiple window systems)


Bridge

(User operations)
Command

Spelling checking & hyphenation


Iterator & Visitor

Overview
Example: WYSIWYG Editor (Cont'd)
Spelling checking & hyphenation
Iterator Visitor

Some more patterns


Template Method Singleton Faade

Observations and Conclusions Further reading

Template Method (Behavioral)


Intent
Define the skeleton of an algorithm in an operation, deferring some steps to subclasses
E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Applicability
To implement invariant aspects of an algorithm once and let subclasses define variant parts To localize common behavior in a class to increase code reuse To control subclass extensions

Template Method (cont'd)


Structure
AbstractClass
E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

+templateMethod() +primitiveOperation1() +primitiveOperation2()

{... primitiveOperation1(); ... primitiveOperation2(); ...}

ConcreteClass +primitiveOperation1() +primitiveOperation2()

Template Method (cont'd)


Consequences
+ Leads to inversion of control (Hollywood principle: don't call us we'll call you) + Promotes code reuse + Lets you enforce overriding rules Must subclass to specialize behavior

E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Implementation
Virtual vs. non-virtual template method Few vs. lots of primitive operations Naming conventions (do- prefix)

Known Uses
Just about all object-oriented systems (especially frameworks)

Template Method (cont'd)


Typical implementation:
public abstract class Node { public final void streamOut (OutputStream out) { if (isReadable()) { doStreamOut(out); } else { doWarning(unreadableWarning); } } // ...

E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

isReadable, doStreamOut, and doWarning are primitive operations

Template Methid - Example

Template Method - Example


class Account { public: void Transaction(float amount); void virtual TransactionSubpartA(); void virtual TransactionSubpartB(); void virtual TransactionSubpartC(); } void Account::Transaction(float amount) { TransactionSubpartA(); TransactionSubpartB(); TransactionSubpartC(); // EvenMoreCode; }
class JuniorAccount : public Account { public: void virtual TransactionSubpartA(); } class SavingsAccount : public Account { public: void virtual TransactionSubpartC(); } // Client code Account* customer; customer = createNewAccount(); customer->Transaction(amount);

Singleton (Creational)
Intent
Ensure a class only ever has one instance, and provide a global point of access to it.
E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Applicability
When there must be exactly one instance of a class, and it must be accessible from a well-known access point When the sole instance should be extensible by subclassing, and clients should be able to use an extended instance without modifying their code

Singleton (cont'd)
Structure

E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Singleton -uniqueInstance : Singleton -singletonData +instance() +singletonOperation() +getSingletonData()

{ return uniqueInstance; }

Singleton (cont'd)
Consequences
+ Reduces namespace pollution + Makes it easy to change your mind and allow more than one instance + Allow extension by subclassing Same drawbacks of a global if misused Implementation may be less efficient than a global Concurrency pitfalls

E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Implementation
Static instance operation Registering the singleton instance

Singleton (cont'd)
Known Uses
Unidraw's Unidraw object Smalltalk-80 ChangeSet, the set of changes to code InterViews Session object

E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Singleton - Example
class Singleton { public: static Singleton* Instance(); protected: Singleton(); Singleton(const Singleton&); Singleton& operator= (const Singleton&) private: static Singleton* pinstance; }; Singleton* Singleton::pinstance = 0; // initialize pointer Singleton* Singleton::Instance () { if (pinstance == 0) // is it the first call? { pinstance = new Singleton; // create sole instance } return pinstance; // address of sole instance } Singleton::Singleton() { //... perform necessary instance initializations }

Singleton - Example
// Client code Singleton *p1 = Singleton::Instance(); Singleton *p2 = p1->Instance(); Singleton & ref = * Singleton::Instance();

Although the above example uses a single instance, modifications to the function Instance() may permit a variable number of instances. For example, you can design a class that allows up to three instances.

Overview
Example: WYSIWYG Editor (Cont'd)
Spelling checking & hyphenation
Iterator Visitor

Some more patterns


Template Method Singleton Faade

Observations and Conclusions Further reading

E. Gamma, R. Helm, R. Johnson, J. Vlissides and Addison-Wesley

Faade

Faade

Faade

Faade - Example

Overview
Example: WYSIWYG Editor (Cont'd)
Spelling checking & hyphenation
Iterator Visitor

Some more patterns


Template Method Singleton Faade

Observations and Conclusions Further reading

Patterns at Different Levels


Applicable in most stages of the OO lifecycle
Analysis, design, implementation, reviews, documentation, reuse, and refactoring

Analysis patterns
Typical solutions to recuring anlysis problems See Analysis Patterns, Fowler; Addison-Wesley, 1996

Architectural patterns
See POSA

Design patterns
Most GoF design patterns are applicable both at the architectural and detailed design

Idioms
Smalltalk Best Practice Patterns, Beck; Prentice Hall, 1997 Concurrent Programming in Java, Lea; Addison-Wesley, 1997 Advanced C++, Coplien, Addison-Wesley, 1992 Effective C++: 50 Specific Ways to Improve Your Programs and Design (2nd Edition), Scott Meyers, Addison-Wesley, (September 1997) More Effective C++: 35 New Ways to Improve Your Programs and Designs, Scott Meyers, Addison-Wesley (December 1995)

Observations
Patterns permit design at a more abstract level
Treat many class/object interactions as a unit Often beneficial after initial design Targets for class refactorings

Variation-oriented design
Consider what design aspects are variable Identify applicable pattern(s) Vary patterns to evaluate tradeoffs Repeat

(Design) Pattern References


The Timeless Way of Building, Alexander; Oxford, 1979; ISBN 0-19502402-8 A Pattern Language, Alexander; Oxford, 1977; ISBN 0-19-501-9199 Design Patterns, Gamma, et al.; Addison-Wesley, 1995; ISBN 0201-63361-2; CD version ISBN 0-201-63498-8 Pattern-Oriented Software Architecture, Buschmann, et al.; Wiley, 1996; ISBN 0-471-95869-7 Analysis Patterns, Fowler; Addison-Wesley, 1996; ISBN 0-20189542-0 Smalltalk Best Practice Patterns, Beck; Prentice Hall, 1997; ISBN 013-476904-X The Design Patterns Smalltalk Companion, Alpert, et al.; AddisonWesley, 1998; ISBN 0-201-18462-1 AntiPatterns, Brown, et al.; Wiley, 1998; ISBN 0-471-19713-0

You might also like