Title: Whats a Pattern Whats an Idiom
1Whats a Pattern? Whats 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
2Gang 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
- Design forces that constrain the solution space
- e.g., Issuing requests to objects without knowing
in advance whats 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
3Simple Pattern Form Example Singleton
- Problem
- Want to ensure a single instance of a class,
shared by all uses throughout a program - Context
- Need to address initialization versus usage
ordering - Solution
- Provide a global access method (e.g., a static
member function in C) - First use of the access method instantiates the
class - Constructors for instance can be made private
- Consequences
- Object is never created if its never used
- Object is shared efficiently among all uses
4A More Complete Pattern Form Command
- Problem
- Want to issue requests to objects
- Dont know in advance which request(s) will be
made - Dont 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 weve seen already
- STL function objects
5Structure Diagram Example Command
- Shows fixed class/interface roles in the pattern
- Shows fixed relationships between roles
client role
command role
execute ( )
inheritance
execute ( )
action(args)
state_
6Collaboration 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
aCommand
anInvoker
aClient
aReceiver
construct
store
time
/ /
/ /
/ /
/ /
execute
action
7Idiom 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
8What is a Pattern Language?
- A pattern resolved some forces
- But may leave others unresolved
- Applying additional patterns helps resolve them
- Repeat until all forces are resolved
- A well-chosen sequence of patterns
- Resolves all design forces adequately
- Is some times called generative
- Self-consistent, can produce/generate a good
design - A pattern language is a narrative
- Of the trade-offs in navigating from requirements
to design - Chapters in Pattern Hatching give small pattern
languages - This is different than a pattern catalog (the GoF
book)
9Pattern-Oriented Design
- Well start by outlining a simple design exercise
(Part I) - Idea maintain a portfolio of stocks and bonds
- Design goals
- Traverse the portfolio and print out each element
- Print out the portfolio in different orders
- Provide a common interface to a single portfolio
instance - Calculate current and projected values of the
portfolio - Well see how key patterns drive the design (Part
II) - Iterator access elements sequentially no matter
how stored - Factory method create a related type
polymorphically - Singleton provides access to a single instance
- Strategy makes behaviors pluggable via common
interfaces - Adapter converts an interface you have into one
you want - Visitor allows interaction with heterogeneous
collections - Well talk about how weve evolved a pattern
language (Part III) - Can be reused different design settings where the
same issues arise
10Part I Design Exercise Outline
- Idea keep track of a portfolio of stocks and
bonds - Abstractly, both stocks and bonds are securities
- Each has a name, a number of shares, a current
value, and a projected value - Stocks and bonds are distinct abstractions,
however - Stocks can have a dividend thats paid out
periodically - Bonds can earn interest thats also paid out
periodically - Design goals
- Traverse the portfolio and print out each element
- Print out the portfolio in different orders
- Provide a common interface to a single portfolio
instance - Calculate current and projected values of the
portfolio
11Basic Abstractions Security, Stock, Bond
- struct Security
- Security (char name,
- int shares,
- int current_value,
- int projected_value)
- virtual Security ()
- char name_
- int shares_
- int current_value_
- int projected_value_
-
- struct Stock public Security
- Stock (char name, int shares,
- int current_value,
- int projected_value,
- int dividend)
- virtual Stock ()
struct Bond public Security Bond (char
name, int shares, int current_value,
int projected_value, int interest)
virtual Bond () int interest_
12Portfolio Abstraction A Collection of Securities
- class Portfolio
- public
- enum error_condition
- not_found 1, already_there
- Portfolio ()
- virtual Portfolio ()
- void add (Security ) // takes ownership
- void remove (Security ) // releases
ownership - void print ()
- int current_value ()
- int projected_value ()
-
- private
-
- deque securities_
- // prevent copy construction, assignment
- Portfolio (const Portfolio )
13Part II Applying Patterns to the Design
- Now well look at how key patterns drive the
design forward - Iterator access elements sequentially no matter
how stored - Factory method create a related type
polymorphically - Singleton provides access to a single instance
- Strategy makes behaviors pluggable via common
interfaces - Adapter converts an interface you have into one
you want - Visitor allows interaction with heterogeneous
collections - Our first challenge is how to iterate through the
collection of securities in the portfolio so that
we can print out its contents - Motivates use of the Iterator pattern here, in
ways that should be familiar - Motivates use of the Factory Method pattern, also
in familiar ways - Well look at each of these patterns first
- Then well look at code that takes advantage of
them
14Iterator Pattern
- Problem
- Want to access aggregated elements sequentially
- E.g., traverse a container of securities and
print them out - Context
- Dont want to know/manage details of how theyre
stored - E.g., could be in a list or an array, but in fact
theyre kept in a deque (nicely balances ease of
sorting, iteration, addition, and erasure) - Solution core
- Provide an 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) - Other examples weve seen before
- C pointers, C STL listiterator
15Factory Method Pattern
- Problem
- You want a type to create another 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., declare abstract method that derived
classes override - E.g., provide traits and common interface as in
the STL (what well use) - Consequences
- Type thats created matches type(s) its used
with - E.g., appropriately positioned dequeiterators are produced by the deque
begin() and end() methods
16Basic Use of Iterator, Factory Method Patterns
- void Portfolioprint ()
-
- for (dequeiterator i
securities_.begin() - i ! securities_.end() i)
- cout shares_ (i)-name_
- (i)-current_value_
- (i)-projected_value_
-
- cout current_value()
- cout projected_value()
- Now onto the next design challenges well address
- Only need a single portfolio instance, want easy
access to it - Well see how the Singleton pattern helps with
this - Want to sort the portfolio in different ways
before printing it - Well see how the Strategy and Adapter patterns
help with this
17Singleton Pattern
- Problem
- Want to ensure a single instance of a class,
thats shared by all uses throughout a program
(e.g., the Portfolio) - Context
- Need to address initialization versus usage
ordering - Solution core
- Provide a global access method (static member
function) - First use of the access method instantiates the
class - Constructors for instance can be hidden (made
private) - Can hide destructor too if a fini method is
also provided - Consequences
- Object is never created if its never used
- Object is shared efficiently among all uses
18Basic Use of the Singleton Pattern
- class Portfolio
- public
- static Portfolio instance()
- static void fini()
-
- private
- static Portfolio instance_
- Portfolio ()
- virtual Portfolio ()
-
-
- Portfolio Portfolioinstance_ 0
- Portfolio Portfolioinstance()
- if (instance_ 0)
- instance_ new Portfolio
-
- return instance_
-
int main (int, char ) try Bond b
new Bond ("City Infrastructure",
10, 2, 3, 5) Stock s new Stock
("Alice's Restaurant",
20, 7, 11, 13) Portfolioinstance()-add
(b) Portfolioinstance()-add (s)
Portfolioinstance()-print ()
Portfoliofini() catch (Portfolioerror_c
ondition e) cout cout -2 return 0
19Strategy Pattern
- Problem
- Want to plug in a family of alternative
parameters to modify behavior (e.g., for sorting
the securities before printing them) - Context
- Need a common interface for the family of
parameters (e.g., less, greater, plus any
parameters we want to define) - Need polymorphic substitution of parameter
objects - Solution core
- Give the different parameter objects a common
interface - Plug these strategies in to modify other behavior
(e.g., sort) - Consequences
- Behavior of algorithms (etc.) is easily modified
- Can extend family of parameters as needed (see
example)
20(Attempted) Basic Use of the Strategy Pattern
- Wed like to have something like the code below
- Although the top part works, the bottom part
doesnt - STL algorithms take arguments by value (class
slicing) - Cant instantiate PrintOrderFunctor due to pure
virtual - Needs a better way to use the abstract base class
- struct PrintOrderFunctor
- virtual PrintOrderFunctor ()
- virtual bool operator () (Security lhs,
Security rhs) const 0 -
- void Portfolioprint (PrintOrderFunctor ppof)
-
- if (ppof)
- sort (securities_.begin(), securities_.end(),
ppof) -
-
-
21Adapter Pattern
- Problem
- We have an interface thats close to (but not
exactly) what we need (cannot use it as is) - Context
- Want to re-use an existing class
- Cant change its interface
- Impractical to extend class hierarchy more
generally - Solution core
- Wrap the interface we have with the interface we
need - Consequences
- For a bit more effort, get reuse of what you
started with
22Basic Use of the Adapter Pattern
- struct PrintOrderFunctor
- virtual PrintOrderFunctor ()
- virtual bool operator () (Security lhs,
Security rhs) const 0 -
- struct PrintOrderFunctorAdapter
- PrintOrderFunctor pof_
- PrintOrderFunctorAdapter (PrintOrderFunctor
pof) pof_(pof) - bool operator () (Security lhs, Security
rhs) return pof_(lhs, rhs) -
- void Portfolioprint (PrintOrderFunctor ppof)
- if (ppof) PrintOrderFunctorAdapter pofa
(ppof) - sort (securities_.begin(),
securities_.end(), pofa) -
-
- One last design challenge (at least for the
moment) - How can we calculate the projected value of the
portfolio? - Need to consider either stock dividend or bond
interest - How can we know which is which when traversing
the securities?
23Visitor Pattern
- Problem
- We have a heterogeneous collection of objects
over which we need to perform type-specific
operations - Context
- Run-time type identification adds overhead and
complexity - 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 - Consequences
- Once modified in this way, any of the types can
handshake with arbitrary visitors to give
correct behavior
24Basic Use of the Visitor Pattern
- struct SecurityVisitor
- virtual SecurityVisitor()
- virtual void
- visit_stock (Stock ) 0
- virtual void
- visit_bond (Bond ) 0
-
- struct Security
-
- virtual void
- accept (SecurityVisitor sv) 0
-
- void
- Stockaccept (SecurityVisitor sv)
- if (sv) sv-visit_stock(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 Portfolioprojected_v
alue () int value 0 for_each
(securities_.begin(),
securities_.end(), ProjectedValueFunct
or(value)) return value
25Part III A Design Pattern Language
- Weve now evolved whats called a pattern
language - A recurring sequence of design patterns that can
be applied to solve a recurring sequence of
design challenges each time you see it - To identify such pattern languages, look for
repetition not only of individual patterns, but
also of combinations and sequences of patterns - Then, look for repetition of the design problems,
and apply the language - This pattern language can be reused when same
issues arise - E.g., any design involving a single collection of
heterogeneous elements - E.g., instead of a portfolio of stocks and bonds,
a zoo of zebras and birds - In parts IV to VI well evolve another design
pattern language - To address additional challenges raised by
multiple interacting agents - Well apply the pattern language to further
extend todays design - Well add multiple agents, each with their own
portfolio - Well add (closed agent-to-agent) cross trading
of securities among them - Well add a market to mediate event triggered
open trading of securities
26From Patterns to Pattern Languages
- So far weve looked at pattern-oriented software
design - We looked for key challenges at each step of the
design process - We matched each challenge with a suitable design
pattern - The pattern let us overcome that challenge and
move on to the next one - This lecture will take that same approach
- New focus additional design issues related to
multi-agent interactions - A fresh look at the Singleton pattern
- New design patterns (from Gamma et al.)
- Well talk about how sets of patterns are
combined/navigated - A look back at the design patterns weve used
this week
27Developing (and Using) a 2nd Pattern Language
- Well further extend our design from last time
(Part I) - Idea add agents who trade stocks and bonds
- Design goals
- Allow multiple agents, each with their own
portfolio, who can trade directly - Allow agents to enter and leave the group of
agents currently trading - Add a market to mediate event triggered open
trading of securities - Well see again how key patterns drive the design
(Part II) - Singleton variant provides key-discriminated
access to a single instance - Prototype a type can produce a duplicate
instance of the same type - Memento package up object state without
violating encapsulation - Command encapsulates a future function call
inside a functor - Observer tell registered observers when state
changes - Well see how weve evolved another pattern
language (Part III) - Can be reused in different design settings where
the same issues arise - I.e., many with interacting agents
(interestingly, even distributed ones)
28Part IV Design Exercise Outline
- Idea add agents who trade stocks and bonds
- We define an agent as a potentially
independently acting software entity (software
engineering notion rather than AI) - Shifts the focus from a single portfolio to the
group of agents - Design goals
- Allow multiple agents, each with their own
portfolio - Support (closed agent-to-agent) cross trading of
securities - Need to consider all of the common data (shares,
current and projected values, name) when testing
securities for equivalence - Assume trades are for all (or none) of the shares
in a security object - Add a market to mediate (event triggered) open
trading - Allow agents to enter and leave the group thats
trading - I.e., they can save and restore their portfolio
and their reserve
29Part V Applying Patterns to the Design
- First challenge each agent needs their own
portfolio - Dont let an agent access anothers portfolio
(security) - However, want to keep previous benefits of using
singleton - Reconsider how we have applied the Singleton
pattern - Need to maintain a separate portfolio instance
per agent - First access by an agent still creates their
specific portfolio - Still want a single global access method, but
index into it - Well use each agents memory address as the
index key - Simplifying assumption Agents memory locations
are hard to infer - Not true actually probe ksizeof(Agent) bytes
away from this - In practice youd use cryptographic keys instead
for secure indexing
30New Variation of the Singleton Pattern
- class Portfolio
- public
- static Portfolio instance(Agent )
- static void fini(Agent )
- ...
- private
- static map instances_
- Portfolio ()
- virtual Portfolio ()
- ...
-
- map Portfolioinstances_
- Portfolio Portfolioinstance(Agent a)
- Portfolio p 0
- mapiterator i
- instances_.find(a)
- if (i instances_.end())
void Portfoliofini(Agent a)
map iterator i
instances_.find(a) if (i ! instances_.end())
Portfolio p i-second
instances_.erase(i) delete p void
Agentbuy (Security s) int cost
s-shares_ s-current_value_ if
(cost reserve_) throw cannot_afford
Portfolioinstance(this)- add(s)
reserve_ - cost AgentAgent ()
Portfoliofini(this)
31Buying and Selling Securities
- Second challenge how to duplicate securities
- We distinguish securities by their common data
but not by their concrete types (or type-specific
data) - I.e., we encapsulate whether a security is a
stock or bond - I.e., use visitor to handshake with it as needed,
otherwise dont care - What if we need to give away a new instance?
- If we dont know a securitys type, do we create
a stock or a bond? - Could rewrite portfolios remove method to work
around this - Remove the security and return a pointer to it
rather than destroying it - However, may want this later (say for extension
to sell part of shares) - Motivates use of the Prototype pattern
- Creates an instance of the original type,
polymorphically - Similar in idea and implementation to Factory
Method pattern - Emulates virtual copy constructor (C doesnt
have that)
32Prototype Pattern
- Problem
- 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
33Use of the Prototype Pattern
- struct Security
- public
-
- virtual Security clone () 0
- ...
-
- Security Stockclone ()
- return new Stock(this)
-
- Security Bondclone ()
- return new Bond(this)
-
Security Agentsell (Security s)
Security current Portfolioinstance(this)
-find(s) if (current 0) throw
cannot_provide Security copy
current-clone() Portfolioinstance(this)-r
emove(current) reserve_ copy-shares_
copy-current_value_ return
copy
34Adding State Persistence
- Third challenge allow agents to depart and
return - Need to save and restore agents portfolio and
reserve - Let agent serialize state to/from a persistent
file, map, etc. - Well only implement the save part for now
- Restore may draw on other patterns we wont cover
(e.g., interpreter) - Motivates use of the Memento pattern
- Serializes agents portfolio and reserve into
opaque cookie - Format of cookie can be tailored to storage
format - Also Motivates use of the Command pattern
- Encapsulates actions on objects within a functor
- Here, provides a different kind of double
dispatch to collect strings representing the
states of individual securities
35Memento Pattern
- Problem
- 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.
36Command Pattern
- Problem
- Want to issue requests to objects
- Context
- Dont know in advance which request(s) will be
made - Dont 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 (generalizes
functions) - Easy to compose existing ones, or add new ones
- Example weve seen already
- STL function objects
37Use of the Memento and Command Patterns
- struct Security
- ...
- virtual string memento () 0
- ...
-
- string Stockmemento ()
- ostringstream oss
- oss
-
-
- string s oss.str() "\n"
- return s
-
- string Bondmemento ()
- ostringstream oss
- oss
-
struct MementoFunctor string str_
MementoFunctor (string str) str_(str)
void operator () (Security sec) str_
sec-memento() string Portfoliomemento
() string s for_each (securities_.begin(),
securities_.end(),
MementoFunctor(s)) return s string
Agentmemento () ostringstream oss oss name_ "\n" Portfolioinstance(this)-
memento() return s
38Use of the Memento Pattern, Continued
void Caretakersave_me(Agent a) if (a
0) return mapiterator
i mementos_.find(a-name()) if (i
mementos_.end()) mementos_.insert(make_pair(
a-name(), a-memento()))
else i-second a-memento()
void Agentsave() Caretakerinstance(
)-save_me(this) void Agentrestore()
Caretakerinstance()-restore_me(this)
- class Caretaker
- public
- static Caretaker instance()
- void save_me(Agent a)
- void restore_me(Agent a)
- private
- Caretaker ()
- Caretaker ()
- map mementos_
- static Caretaker instance_
-
39Adding a Market
- Fourth challenge need to coordinate market,
agents - Agents independently choose when (and whether) to
trade - Whenever a trade is made in the market, agents
are notified - Motivates use of the Observer pattern
- Helps to keep agents independent
- Separates registration, notification, trading
interactions - Allows coordination between market and the agents
40Observer Pattern
- Problem
- 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
41Use of the Observer Pattern
- class Market
- public
- static Market instance()
- void bind(Agent )
- void unbind(Agent )
- void make_advertisement()
- void fetch_advertisements()
- private
- ...
- void notify ()
- set observers_
-
- void Marketbind(Agent a)
- setiterator i observers_.find(a)
- if (i observers_.end()) observers_.insert(a)
-
- void Marketunbind(Agent a)
- setiterator i observers_.find(a)
- if (i ! observers_.end())
void Marketmake_advertisement()
notify() void Marketfetch_advertisements()
... void Marketnotify () for (setiterator i observers_.begin()
i ! observers_.end() i)
(i)-update() AgentAgent (const char
name, int reserve) name_(name),
reserve_(reserve) Marketinstance()-bind(th
is) AgentAgent () Marketinstance()-u
nbind(this) Portfoliofini(this) void
Agentupdate() Marketinstance()-fetch_adve
rtisements()
42Part VI 2nd Design Pattern Language
- Weve evolved another pattern language
- A recurring sequence of design patterns that can
be applied to solve a recurring sequence of
design challenges each time you see it - Think of the patterns as a design vocabulary
that you can use - E.g., combine command, proxy, and memento to
serialize an object, send it from one computer to
another across a socket, re-constitute it - This pattern language can be reused when same
issues arise - E.g., designs for multiple interacting agents
with similar requirements - E.g., instead of agents and a market, client and
server computers - Gamma et al. (GoF Book) takes this idea of reuse
even farther - Shows how the patterns can be collected into a
design evolution map - Describes how the resulting pattern map can be
reused
43Summary
- Weve now looked at quite a few patterns
- Iterator access elements sequentially no matter
how stored - Factory method create a related type
polymorphically - Singleton provides access to a single instance
(possibly per index) - Strategy makes behaviors pluggable via common
interfaces - Adapter converts an interface you have into one
you want - Visitor allows interaction with heterogeneous
collections - Prototype allows polymorphic duplication of
heterogeneous types - Memento packages up object state without
violating encapsulation - Command packages up a function as an object
- Observer tell registered observers when state
changes - More importantly weve looked at how they can
drive design - From basic abstractions towards a working program
(despite obstacles) - A design vocabulary and related sequences
within it - CSE 432 focuses on combining patterns of this
sort (design) - CSE 532 focuses on other kinds of patterns
(architectural)