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