What’s a Design Pattern? A design pattern has a name So when someone says “Adapter” you know what they mean So you can communicate design ideas as a “vocabulary” A design pattern describes the core of a solution to a recurring design problem So you don’t have to reinvent known design techniques So you can benefit from others’ (and your) prior experience A design pattern is capable of generating many distinct design decisions in different circumstances So you can apply the pattern repeatedly as appropriate So you can work through different design problems using it
Iterator Pattern Problem Context Solution core Consequences Example Want to access aggregated elements sequentially E.g., traverse a container of values, objects etc. and print them out Context Don’t want to know/manage details of how they’re stored E.g., could be in an array, list, vector, or deque Solution core Provide a common interface for iteration over a container: (1) start; (2) access; (3) increment; (4) termination Consequences Frees user from knowing details of how elements are stored Decouples containers from algorithms (crucial in C++ STL) Example list<int>::iterator
Factory Method Pattern Problem You want a type to create a related type polymorphically E.g., a container should create appropriate begin and end iterators Context Each type knows which related type it should create Solution core Polymorphic creation E.g., abstract method that different types override E.g., provide traits and common interface (as in the STL we’ll use) Consequences Type that’s created matches type(s) it’s used with Example vector<double> v; vector<double>::iterator i = v.begin();
Three Design Patterns Involving Classes Adapter (structural) Converts an interface you have into one you want Memento (behavioral) Externalizes the state of an object Observer (behavioral) Tells objects about changes in another object
Structural Patterns Help define fixed structural relationships Between classes, and their associations Emphasis on the class diagrams Example Adapter pattern
Inconsistent Interfaces Challenge Different code bases are often written by different developers, at different times, with different design goals Interfaces expected by one piece of code are not always the same as those provided by the other code that’s available Motivates use of the Adapter pattern Provides a class that exposes the interface that’s needed That interface is implemented using calls to methods of the existing object(s) A related pattern called “Wrapper Façade” provides a similar capability by wrapping functions (e.g., socket calls, etc.)
Adapter Pattern Problem Context Solution Core Have an object with an interface that’s close to (but is not exactly) what we need Context Want to re-use an existing class Can’t change its interface It’s impractical to extend class hierarchy more generally Solution Core Wrap a particular class or object with the interface needed (2 forms: class form and object forms)
Adapter Structure (Class Form) Interface Impl method () = 0; impl_method (); public private Adapter method () { impl_method (); }; Interface abstract base class provides desired interface Impl concrete class provides the implementation Adapter glues them together via inheritance
Adapter Structure (Object Form) Interface method () = 0; impl_ Adapter Impl method () { impl_->impl_method(); }; impl_method (); Interface abstract base class provides desired interface Impl concrete class provides the implementation Adapter glues them together via delegation
Behavioral Patterns Help define dynamic behavioral relationships Between objects at run-time Emphasis is on interactions among objects Examples Memento pattern Observer pattern
Adding State Persistence Challenge Want to save and restore an object’s state For example, between different runs of a program Motivates use of the Memento pattern Serialize an object’s state into an opaque “cookie” Format of cookie can be tailored to storage format Can send it to a file, a caretaker object, another computer Can reconstitute object from its memento later/elsewhere
Memento Pattern Problem Context Solution Core Consequences Want to externalize state of an object without violating encapsulation Context A snapshot of object state is needed Providing a state interface would violate encapsulation Solution Core Create a memento class with methods to get, set state Provide an opaque representation of state itself Consequences Can use memento to send object state over a socket, save it in a file, put it into a checkpoint/undo stack, etc.
Inter-dependent Object Behaviors Challenge: need to coordinate object state changes For example, a sensor may record current temperature Other objects (e.g., thermostat) need to know when the temperature changes Motivates use of the Observer pattern Helps to keep objects mostly independent Separates registration, notification, and actions But, still allows appropriate coordination among objects
Observer Pattern Problem Context Solution core Consequences Need to update multiple objects when the state of one object changes Context Multiple objects depend on the state of one object Set of dependent objects may change at run-time Solution core Allow dependent objects to register with object of interest, notify them of updates when state changes Consequences When observed object changes others are notified Useful for user interface programming, other applications
Observer Pattern Behavior <<Subject>> <<Observer>> <<Observer>> register() register() (optional) ∆ state notify() update() update()
Three More Design Patterns Singleton (creational) Provides access to a single (or indexed) instance Prototype (creational) Polymorphic duplication of heterogeneous types Visitor (behavioral) Allows interaction with heterogeneous collections Compile time dispatching is standard, but can emulate with dynamic type casting or RTTI
Creational Patterns Help define how objects are created/initialized May emphasize class or interaction diagrams Examples Factory Method Singleton Prototype
Single Global Instance Challenges Only one object of a type is needed or allowed in a program Whether or not the object is needed at all may vary Need to make sure the object is initialized before first use Motivates use of the Singleton pattern Instantiates the object on-demand (if requested) If no request is made, the object is never created (efficient) Makes it easy to obtain an alias from anywhere in program Don’t need to pass a reference/pointer up and down the call stack Initializes object before first alias to it is handed out Lifetime of the object covers interval of its possible use
Singleton Pattern Problem Context Solution core Consequences Want to ensure a single instance of a class, that’s shared by all uses throughout a program (e.g., the Portfolio, the Zoo) Context Need to address ordering of initialization versus usage E.g., usage from different source files than where the object is defined Solution core Static pointer member variable is initialized to 0 (before main starts) Provide a global access method (static member function) First use of the access method instantiates object, updates pointer Constructors for instance can be hidden (made private) Can hide destructor too if a “fini” or “destroy” method is also provided Consequences Object is never created if it’s never used Object is shared efficiently among all uses
Basic Use of the Singleton Pattern class Portfolio { public: static Portfolio * instance(); static void fini(); // . . . private: static Portfolio * instance_; Portfolio (); virtual ~Portfolio (); }; Portfolio * Portfolio::instance_ = 0; Portfolio * Portfolio::instance() { if (instance_ == 0){ instance_ = new Portfolio; } return instance_; void Portfolio::fini() { delete instance_; instance_ = 0; int main (int, char * []) { try { Stock *s = new Stock ("Alice's Restaurant", 20, 7, 11, 13); Bond *b = new Bond ("City Infrastructure", 10, 2, 3, 5); Portfolio::instance()->add (s); Portfolio::instance()->add (b); Portfolio::instance()->print (); Portfolio::fini(); } catch (Portfolio::error_condition &e) { cout << "Portfolio error: " << e << endl; return -1; catch (...) { cout << "unknown error" << endl; return -2; return 0;
An Indexed Variation of the Singleton Pattern class Portfolio { public: static Portfolio * instance(Agent *); static void fini(Agent *); ... private: static map<Agent *, Portfolio *> instances_; Portfolio (); virtual ~Portfolio (); }; map<Agent *, Portfolio *> Portfolio::instances_; Portfolio * Portfolio::instance(Agent *a) { Portfolio * p = 0; map<Agent *, Portfolio *>::iterator i = instances_.find(a); if (i == instances_.end()) { p = new Portfolio; instances_.insert(make_pair(a,p)); } else { p = i->second; } return p; } void Portfolio::fini(Agent *a) { map<Agent*,Portfolio*>:: iterator i = instances_.find(a); if (i != instances_.end()) { Portfolio * p = i->second; instances_.erase(i); delete p; } void Agent::buy (Security *s) { int cost = s->shares_ * s->current_value_; if (cost > reserve_) { throw cannot_afford; Portfolio::instance(this)-> add(s); reserve_ -= cost; Agent::~Agent () { Portfolio::fini(this);
Polymorphic (Deep) Copying Challenges C++ does not have a virtual copy constructor However, may need to duplicate polymorphic collections All the securities in a portfolio (which are actually stocks or bonds) All the animals in a zoo (which are actually Giraffes or Ostriches) Copy construction depends on the concrete types involved Motivates use of the Prototype pattern Wraps copy construction within a common virtual method Each subclass overrides that method: uses its specific copy constructor with dynamic allocation to “clone” itself
Prototype Pattern Problem Context Solution core Consequences Need to duplicate objects with different dynamic types Context Virtual constructors are not available (e.g., in C++) However, polymorphic method invocations are supported Solution core Provide a polymorphic method that returns an instance of the same type as the object on which the method is called Polymorphic method calls copy constructor, returns base class pointer or reference to concrete derived type Consequences Emulates virtual copy construction behavior Allows anonymous duplication of heterogeneous types
Use of the Prototype Pattern struct Security { public: … virtual Security * clone () = 0; ... }; Security * Stock::clone () { return new Stock(*this); } Security * Bond::clone () { return new Bond(*this); Security * Agent::sell (Security *s) { Security * current = Portfolio::instance(this)->find(s); if (current ==0) { throw cannot_provide; } Security * copy = current->clone(); Portfolio::instance(this)->remove(current); reserve_ += copy->shares_ * copy->current_value_; return copy;
Interacting with Heterogeneous Collections Challenges Polymorphism lets you aggregate different types together However, how to interact depends on the specific types Motivates use of the Visitor pattern Lets the program discover how to interact with each concrete type through an initial “handshake” with it Dispatches that interaction directly through a method call
Visitor Pattern Problem Context Solution core Consequences We have a heterogeneous collection of objects over which we need to perform type-specific operations Context Run-time type identification (if available) adds overhead Want to avoid unnecessary interactions among types Types in collection change less frequently than the set of operations that are to be performed over them Solution core Modify types in the collection to support double dispatch If you cannot, RTTI / dynamic casting can be used similarly (a variant?) Consequences Once modified in this way, any of the types can handshake with arbitrary “visitors” to give correct behavior
Basic Use of the Visitor Pattern struct SecurityVisitor { virtual ~SecurityVisitor(); virtual void visit_stock (Stock *) = 0; visit_bond (Bond *) = 0; }; struct Security { … accept (SecurityVisitor * sv) = 0; void Stock::accept (SecurityVisitor * sv) { if (sv) {sv->visit_stock(this);} } Bond::accept (SecurityVisitor * sv) { if (sv) {sv->visit_bond(this);} struct ProjectedValueFunctor : public SecurityVisitor { int & value_; ProjectedValueFunctor (int & value); virtual ~ProjectedValueFunctor (); void operator () (Security * s) { s->accept(this); } virtual void visit_stock (Stock * s) { if (s) {value_ += s->shares_ * (s->projected_value_ + s->dividend_);} virtual void visit_bond (Bond * b) { if (b) {value_ += b->shares_ * (b->projected_value_ + b->interest_);} }; int Portfolio::projected_value () { int value = 0; for_each (securities_.begin(), securities_.end(), ProjectedValueFunctor(value)); return value;
Additional Resources Design Patterns Book Design Patterns Wiki