Download as ppt, pdf, or txt
Download as ppt, pdf, or txt
You are on page 1of 23

Introduction to Design Patterns

• Design patterns were mentioned several times so far


– And the Singleton Pattern was discussed in detail
– Along with several C++ memory management idioms
• The lectures this week will focus on them in detail
– What they are and how they are structured
– Introduction to the main “Gang of Four” design patterns
• The first set of design patterns should feel familiar
– They use or help support C++ features we’ve discussed
– We’ve seen examples of some of them already
• The second set is useful for advanced design topics
– Different ways to set up how a group of objects interacts
– How to send an object from one machine to another

CSE 332: Design Patterns (Part I)


What’s a Pattern? What’s an Idiom?

• According to Alexander, a pattern:


– Describes a recurring problem
– Describes the core of a solution
– Is capable of generating many distinct designs

• An Idiom is more restricted


– Still describes a recurring problem
– Provides a more specific solution, with fewer variations
– Applies only to a narrow context
• e.g., the C++ language

CSE 332: Design Patterns (Part I)


“Gang of Four” Pattern Structure
• Gang of Four (GoF): Gamma, Johnson, Helm, Vlissides
– Authors of the popular “Design Patterns” book
• A pattern has a name
– e.g., the Command pattern
• A pattern documents a recurring problem
– e.g., Issuing requests to objects without knowing in advance what’s to
be requested or of what object
• A pattern describes the core of a solution
– e.g., class roles, relationships, and interactions
– Important: this is different than describing a design
• A pattern considers consequences of its use
– Trade-offs, unresolved forces, other patterns to use

CSE 332: Design Patterns (Part I)


Simple Pattern Form Example: “Singleton”
• Problem
– Want to ensure a single instance of a class, shared
throughout a program
• Context
– Need to address initialization versus usage ordering
• Solution
– Provide a global access method (static in C++)
– First use of the access method instantiates the class
– Constructors for instance can be made private
• Consequences
– Object is never created if it’s never used
– Object is shared efficiently among all uses

CSE 332: Design Patterns (Part I)


Singleton Class Template
• Parameterized by a template <class T> class Singleton {
concrete type public:
static T *instance();
• Notice constructor and private:
variable are private Singleton();
static T *instance_;
• Initialization of static };
s_instance variable // Initialize the static instance pointer
– Outside class declaration template <class T>
T *Singleton::instance_ = 0;
– Outside method definition
– Done before any method in // Global access point
compilation unit is called template <class T>
• Instance accessor method T *Singleton::instance() {
can then check // check for existing instance
– For a 0 instance pointer if (Singleton<T>::instance_ == 0) {
// may want a try/catch here
– And create a new instance if
Singleton<T>::instance_ =
so
new Singleton<T>;
• Same object is always }
returned by accessor return Singleton<T>::instance_;
};

CSE 332: Design Patterns (Part I)


Using the Singleton Template
• Need a single
instance
– E.g., a common Foo *f1 =
Singleton<Foo>::instance();
buffer of text tokens Foo *f2 =
from a file Singleton<Foo>::instance();

• Shared across
multiple points in the
code
• Need to share buffer
– Copying is wasteful
– Need to refer to
same object instance
• What about deleting
these instances?
CSE 332: Design Patterns (Part I)
A More Complete Pattern Form: “Command”
• Problem
– Want to issue requests to objects
– Don’t know in advance which request(s) will be made
– Don’t know in advance to what object(s) they will go
• Solution core
– Encapsulate function call parameters and target object
reference inside an “execute” method
• Consequences
– Decouples invocation/execution
– Commands are first-class objects (elevates functions)
– Easy to compose, add new ones
• Example we’ve seen already
– STL function objects
CSE 332: Design Patterns (Part I)
Structure Diagram Example: “Command”
• Shows fixed class/interface roles in the pattern
• Shows fixed relationships between roles
<<Client>> client role command role

<<Invoker>> * <<Command>>
execute ( )

inheritance
<<ConcreteCommand>>
<<Receiver>> execute ( )
action(args) state_

CSE 332: Design Patterns (Part I)


Collaboration Diagram Example: “Command”
• Shows dynamic interactions between pattern roles
– Labels show what interaction does (here, labels show methods called)
• Often used to diagram each of several key scenarios
– “Happy path” when everything works, plus different error cases

aClient aReceiver aCommand anInvoker


new Command(aReceiver)

StoreCommand(aCommand)
time

/ / / / / / / /
action() execute()

CSE 332: Design Patterns (Part I)


Pattern Example: Command
class OpenCommand : public Command {
public:
OpenCommand(Application*);
virtual void Execute();
protected:
virtual const char* AskUser();
private:
class Command { Application* application;
public: char* _response;
virtual ~Command(); };
virtual void Execute() = 0;
OpenCommand::OpenCommand(Application* a){
_application = a;
protected: }
Command();
}; void OpenCommand::Execute () {
const char* name = AskUser();
if (name != NULL) {
Document* doc = new Document(name);
_application->Add(doc);
doc->Open();
}
}

CSE 332: Design Patterns (Part I)


Idiom Example: Guard
• Problem
– Want to tie key scoped behaviors to actual program scopes
• e.g., program trace, resource acquisition/release, locking
– However, tying functions to functions is error-prone
• e.g., forgetting the release call, exceptional return paths
• Solution
– Design a special adapter class whose constructor and
destructor call the key scope entry and exit behaviors
– Create a guard object on the program call stack (in a scope)
• Context limitations
– Mainly limited to languages with constructor/destructor

CSE 332: Design Patterns (Part I)


Part I: Familiar Design Patterns
• First, a few more patterns related to course so far
– Iterator: access elements sequentially no matter how stored
– Adapter: converts interface you get into one you want
– Factory method: creates a related type polymorphically

• Next lecture we’ll examine other important patterns


– Bridge: allow interface, implementation to vary separately
– Chain of responsibility: give request to chain of handlers
– Composite: common interface to composite/simple objects
– Interpreter: build a representation for a simple language
– Observer: tell registered observers when state changes
– Strategy/template method: vary steps/all of an algorithm
– Proxy: forward requests from placeholder to another object
– Memento: package up object state without violating encapsulation
– Visitor: allow various operations on fixed set of objects (2-way dispatch)
CSE 332: Design Patterns (Part I)
Iterator Pattern
• Problem
– Want to access aggregated elements sequentially
• E.g., traverse a list of names and print them out
– Don’t want to know details of how they’re stored
• E.g., in a linked list, or an array, or a balanced binary tree
• Solution core
– Provide a separate interface for iteration over each container
• Consequences
– Frees user from knowing details of how elements are stored
– Decouples containers from algorithms (crucial in C++ STL)
• Examples we’ve seen before
– C++ pointers, C++ STL list<int>::iterator

CSE 332: Design Patterns (Part I)


Iterator Pattern Structure Diagram
• Each container may have a different iterator type
• Iterator knows the internals of the container
• Object-oriented form shown below (for user-defined types)
• Slightly different with built-in types, templates
– E.g., no inheritance relationship, may use traits, etc.
<<Iterator>>

first()
next()
<<Container>> is_done()
create_iterator() current_item()

<<ConcreteAggregate>>
creates <<ConcreteIterator>>
has-a
CSE 332: Design Patterns (Part I)
Object-Oriented Iterator Example
class StringIterator {
public:
• Object-oriented version of
iterator is natural to implement
StringIterator (char * s) as a class in C++
: s_ (s), current_ (s) {}
• Constructor stores passed
void first () { pointer to C-style string s,
current_ = s_;
} positions current_ at s
• first (re)positions iterator at
void next () {
++current_; the start of the string
} • next moves iterator to the next
bool is_done () { position
return *current_ == 0; • is_done tests whether iterator
}
is at the end of the string
char * current_item () {
return current_;
• current_item returns a pointer
} to the character at the current
iterator position
private:

char *s_;
char *current_;

};
CSE 332: Design Patterns (Part I)
Using an Object-Oriented Iterator
unsigned int letter_count • Iterators naturally support
(StringIterator& si, char c) use in looping constructs
{
unsigned int count = 0;
• Here we show a for loop
for (si.first (); – first is used to initialize
! si.is_done ( ); – is_done used for loop test
si.next ()) – next used to increment
if (*si.current_item () == c) – current_item is used in
++count; loop body’s value comparison
return count; • When the loop completes,
}
the function shown has
– Iterated through entire string
– Counted occurrences of the
character value in the string
– Returned the occurrence
count

CSE 332: Design Patterns (Part I)


Using a Procedural Iterator.

unsigned int letter_count • This pattern is not limited to


(char * si, char * start, char c) C++ or even to object-oriented
{ languages
unsigned int count = 0; – For example, C code to the left
for (si = start; • Some changes are needed
*si != 0; – Need to pass in start of string
++si) (not encapsulated in iterator
if (*si == c) class)
++count; – Syntax for pointer operators
return count; instead of method invocations
} • Solution core clearly the same
– Program pretty much the same
– Result of function is the same
– Looks a lot like STL examples

CSE 332: Design Patterns (Part I)


Adapter Pattern
• Problem
– Have an object with an interface that’s close to but not
exactly what we need
• Context
– Want to re-use an existing class
– Can’t change its interface
– Impractical to extend class hierarchy more generally
• Solution
– Wrap a particular class or object with the interface
needed (2 forms: class form and object forms)
• Consequences
– Implementation you’re given gets interface you want

CSE 332: Design Patterns (Part I)


Adapter Pattern Structure Diagram (Class Form)

Interface Impl
method () = 0; impl_method ();

public private

Adapter
method ();

• Abstract base class provides desired interface


• Concrete Impl class provides the implementation
• Adapter glues them together via inheritance

CSE 332: Design Patterns (Part I)


Adapter Pattern Structure Diagram (Object Form)

Interface
method () = 0;

Adapter Impl
method (); impl_method ();

• Abstract base class provides desired interface


• Concrete Impl class provides the implementation
• Adapter class glues them together via delegation

CSE 332: Design Patterns (Part I)


Factory Method Pattern
• Problem
– You want a class to
Container
Iterator create a related class
first()=0; polymorphically
Iterator * next()=0;
make_iterator()=0; current()=0; • Context
is_done()=0; – Each class knows
which version of the
related class it should
create
Array Array_Iterator • Solution
first(); next();
Iterator * – Declare abstract
current();
make_iterator();
is_done(); method that derived
classes override
Linked_List List_Iterator • Consequences
first();next();
Iterator *
current(); – Type created matches
make_iterator(); type(s) it’s used with
is_done();

CSE 332: Design Patterns (Part I)


Object-Oriented vs. Generic Variations
• Use Factory Method pattern if you have inheritance
hierarchies of classes (with abstract base classes)
• Use the Traits idiom when you want a generic
programming version of this technique
– E.g., when you have unrelated classes capable of providing
common interfaces (like STL vectors and linked lists)
• Traits version relies on templates and compile-time
dispatching of calls
• Inheritance based version relies on run-time dynamic
resolution of calls (virtual functions and overriding)

CSE 332: Design Patterns (Part I)


Summary
• We’ve looked at a number of important patterns so far
– Singleton: share a class instance across multiple uses
– Command: package up a function as an object
– Iterator: access elements sequentially no matter how stored
– Adapter: converts interface you get into one you want
– Factory method: creates a related type polymorphically
• Next time: Design Patterns II
– Bridge: allow interface, implementation to vary separately
– Chain of responsibility: give request to chain of handlers
– Composite: common interface to composite/simple objects
– Interpreter: build a representation for a simple language
– Observer: tell registered observers when state changes
– Strategy/template method: vary steps/all of an algorithm
– Proxy: forward requests from placeholder to another object
– Memento: package up object state without violating encapsulation
– Visitor: allow various operations on fixed set of objects (2-way dispatch)

CSE 332: Design Patterns (Part I)

You might also like