Design Patterns Dennis Mancl Alcatel-Lucent – Bell Labs November 28, 2007

Transcription

Design Patterns Dennis Mancl Alcatel-Lucent – Bell Labs November 28, 2007
Design Patterns
Dennis Mancl
Alcatel-Lucent – Bell Labs
November 28, 2007
1
1
Design Patterns Outline
• What is a pattern?
– The Design Patterns Book
– One example pattern: Singleton
– Patterns versus Idioms
•
•
•
•
•
•
Wrapping with Facade objects
Remote access with Proxy
Observer
Analysis patterns
Architectural patterns
Enterprise Architecture patterns
2
2
What is a pattern?
• A pattern is a solution to a
problem in a context
• The problem and context
come from some domain
(software development,
project management, …)
– The problem and context
contain some “unresolved
forces” that need to be
addressed.
• The solution is a proposed
way to resolve the problem
Name: Information Expert
Problem: How to decide which
class or object to assign
responsibilities to?
Context: A responsibility usually
requires some information or data
for its fulfillment – information about
other objects, an object’s own state,
the world around an object, and so
on.
Solution: Assign a responsibility to
the class that has the information
needed to fulfill it.
(from Applying UML and Patterns, third edition,
Craig Larman)
3
Also see the description of “Pattern Language” in the Wikipedia -http://en.wikipedia.org/wiki/Pattern_language.
3
What is a pattern?
• Patterns are used
to capture
knowledge and
experience – for
example, what to
think about when
making design
choices
• Not a cookbook
• Each pattern
might trigger
others
Name: Loose Interfaces
Problem: To avoid development bottlenecks, we
need to be able to limit the effect that one team’s
work will have on another.
Context: Interfaces need to be somewhat flexible if
the teams are working quickly. Requirements may be
changing rapidly, and it may be difficult to
communicate between geographically distributed
subteams.
Solution: Limit the number of explicit, static
interfaces. Define larger-grained interfaces that allow
developers to code against interfaces defined early,
but that do not overly constrain functionality.
Consider using the Hierarchy of Factories pattern.
(from Organizational Patterns of Agile Software Development, Coplien and
Harrison)
4
4
Patterns in non-software domains
• Name: ChocolateChipRatio
• Context: You are baking chocolate chip cookies in small batches for
family and friends
• Consider these patterns first: SugarRatio, FlourRatio, EggRatio
• Problem: Determine the optimum ratio of chocolate chips to cookie
dough
• Solution: Observe that most people consider chocolate to be the
best part of the chocolate chip cookie. Also observe that too much
chocolate may prevent the cookie from holding together, decreasing
its appeal. Since you are cooking in small batches, cost is not a
consideration. Therefore, use the maximum amount of chocolate
chips that results in a sturdy cookie.
• Consider next: WalnutRatio or CookingTime or FreezingMethod
(from Wikipedia: http://en.wikipedia.org/wiki/Pattern_language)
5
This example shows that there are multiple patterns in a Cookie pattern language, but this
pattern focuses on one of the critical characteristics of good chocolate chip cookies – getting
the amount of chocolate right. Personally, I always follow the Nestle recipe, which is the
recipe on the bag of Nestle’s toll house morsels – their ratio is just about right to satisfy the
competing forces of good taste and solid cookie architecture.
5
Software patterns
• The first “Object Oriented Design
Patterns” are found in the book
– Design Patterns by Erich Gamma,
Richard Helm, Ralph Johnson, and John
Vlissides (known as “Gang of 4” or “GOF”)
– 23 patterns: three categories (Creational,
Structural, Behavioral)
– Problems:
• control the construction of objects
• create organized collections of objects
• divide responsibilities among objects for
better encapsulation and easier
modification
– Examples in C++ and Smalltalk
– The patterns also apply to other
languages (Java, Visual Basic, Perl)
6
The Design Patterns book first appeared in fall 1994. The book was a big hit at the OOPSLA
conference (sponsored by ACM SIGPLAN) – the Addison-Wesley booth was completely sold
out of copies of the book.
Many of the other books on design patterns make reference to these 23 patterns – either by
repeating them, extending them, or discussing some specializations of the patterns for certain
contexts.
6
Singleton Pattern
• Name: Singleton
• Problem: We need to restrict the creation of new objects
for a particular class: only one instance.
• Context: Other objects in the design need a single point
of access to use the object.
• Solution: Define a “static member function” within the
class that returns a pointer (or reference) to the
Singleton object. The static member function will create
the object the first time it is called. All constructors are
made private to prevent other objects from being
created.
7
Singleton is the simplest pattern in the book. It is relatively easy to implement in C++.
The access to the object is controlled: by putting the “instance” in the private section of the
class, any function outside of the class can’t directly access the object – the access must be
done through the advertised interface (the getInstance() static member function).
Note: The actual “construction” of the singleton object is deferred until the first time the
application tries to access it – see the next page.
7
More about Singleton
• Simple version of Singleton (in C++, without multi-thread support):
MySingletonClass
– instance : MySingletonClass *
other attributes …
+ getInstance() : MySingletonClass *
– MySingletonClass()
other operations …
underline means
static data member
if (instance == 0) {
instance =
new MySingletonClass();
}
return instance;
• Access the Singleton like this: MySingletonClass::instance()
• Different implementations of the Singleton pattern for Java,
multithreaded support, allocation in special memory, and so on.
8
Suppose you need a Singleton in a multi-threaded context. In that case, the instance == 0 test
plus the call to the new operator in the getInstance() function is a “critical section”. If two
threads try to do this operation at the same time, the results are unpredictable. You *might*
wind up with two instances of the singleton object.
A solution to this problem can be found in Design Patterns Explained by Alan Shalloway and
James R. Trott.
if (instance == 0) {
lock.acquire();
if (instance == 0) {
instance = new MySingletonClass();
}
lock.release();
}
return instance;
8
Patterns and Idioms
• What have we done? We have used a “design trick” that
solves a specific problem
• Some standard design tricks are called “idioms”
– they tend to be small, tricky, and language specific
– example: virtual destructors in C++
– example: anonymous inner classes in Java
• How are patterns different from idioms?
– Patterns are more about design
– Singleton is useful in Java, Smalltalk, Visual Basic, Perl, …
– different “realizations” of patterns in different languages
9
There is some overlap between idioms and design patterns. But for the most part, idioms are
aimed at more concrete circumstances, and they tend to be written as “rules”:
•
define the copy constructor, assignment operator, and destructor for C++ classes that
manage dynamic resources (for example, classes that allocate dynamic memory
regions, open file descriptors, or open databases)
•
always define a virtual destructor for C++ classes that contain other virtual functions
•
never return a reference to an object on the stack in a C++ function
9
Patterns in the Design Patterns book
• 23 Design Patterns: three categories
Creational
Structural
Abstract
Factory
Adapter
Chain of
Responsibility
Observer
Builder
Bridge
Command
State
Interpreter
Strategy
Factory Method Composite
Behavioral
Prototype
Decorator
Iterator
Template
Method
Singleton
Facade
Mediator
Visitor
Flyweight
Memento
Proxy
10
10
Sources of information
• So, you want to learn how to use these design patterns
– to make your OO designs better
• Where to look to learn more:
– the book
– A Learning Guide to Design Patterns
(http://www.industriallogic.com/papers/learning.html)
– Pattern Stories Wiki (http://wiki.cs.uiuc.edu/PatternStories)
– http://c2.com/cgi/wiki?DesignPatterns
– other books:
•
•
•
•
Pattern-Oriented Software Architecture by Frank Buschmann et. al.
Design Patterns Java Workbook by Steven John Metsker
Refactoring to Patterns by Joshua Kerievsky
Design Patterns Explained by Alan Shalloway and James R. Trott
11
New books, articles, and courses on patterns are being written every day.
If you are an ACM member (ACM is Association for Computing Machinery), you can take
free online training courses, including a introduction to Design Patterns:
http://pd.acm.org/course_slc.cfm?pre=ITDT&trk=654&crs=WT-1230-180
11
Why learn the Design Patterns?
• Your own designs will improve
– borrowing well-tested ideas
– pattern descriptions contain some analysis of tradeoffs
• You will be able to describe complex design ideas to
others
– assuming that they also know the same patterns
• You can use patterns to “refactor” existing code
– refactoring is improving the structure of existing code without
adding new functionality
– some techniques for transforming and adapting legacy code are
based on design patterns
12
12
Facade Pattern
• Problem: The application needs a simple interface to a
complex subsystem
– the subsystem might have been written by someone else
– you don’t want the entire development team to go back to the
subsystem documentation with lots of questions
• Context:
– it is important to control the dependencies between the application and
the complex subsystem – you want to reduce the effort to maintain the
system
• Solution: Define a single Facade class
– the Facade class has knowledge of the internal details of the
subsystem, but the Facade class provides a simple to use interface for
the application
– each Facade public function might call many subsystem operations
13
13
Facade diagram
•
A Facade class wraps a bunch of operations on other classes (or a
bunch of legacy code operations) into a convenient package
application
application calls
some of the Facade
class operations
Facade class
scanner
pointers to other data
objects within a
subsystem
get_val1()
build_trans2()
do_trans3()
commit_trans4()
the Facade accesses
some of the internals
of a complex subsystem to
implement its operations
database
interface
parser
formatter
14
A Facade class has some interesting internal structure:
• the data section of a Facade class is usually more than just a simple pointer
• the implementation of the Facade member functions might need to interact with
many classes and/or functions in the subsystem that it is hiding
The Facade pattern is the most commonly used pattern for burying a non-OO legacy
subsystem.
14
Planning a Facade
• Part of the OO design process: create a simplified
model of the classes in the overall system
– using CRC cards (informal technique using index cards, one
card per class)
– using UML Class Diagrams
• Look for a group of classes that collaborate closely
together: call them a “subsystem”
• Try to “put the subsystem behind a wall” – define a
single interface class that defines an API for the
subsystem
15
15
Proxy example
•
A Proxy object is a “stand-in” for an object that might be:
– located on another machine (example: CORBA objects, Java RMI)
– or it might be large and complicated, so you want to defer building it
– or it might require a special access method (to check the users’
permissions)
CORBA example
client application
Stub objects have full
public interface, but no data.
They just push a message
over to the server.
ORB (server)
CORBA
IDL stubs
IDL skeletons
Interface
Repository
Object
Implementations
network
requests
responses
Generated
by a tool
Developers
fill in details
16
In the Proxy pattern, the Proxy object doesn’t necessarily have a simple way to access the
information in the class that stands behind the Proxy object: it might involve a complex
communication protocol instead of just following a pointer.
For example, CORBA implementations usually generate client-side “stub” classes (which are
simple Proxy classes) directly from an IDL specification of a class). The operations of these
stub classes will send messages to the “real” object implementations that live on a server
machine. The Object Request Broker (ORB) on the client machine knows how to parse the
messages from stub classes. The ORB will pick out the right operations from the CORBAgenerated code (the IDL skeletons and Interface Repository) and the user-constructed code
(the Object Implementations) to return the right information to the client.
Examples of non-distributed-object related Proxy objects include:
• bitmap objects (creating or downloading a bitmap might be deferred until the
application knows what section of the bitmap is needed)
• sparse matrix objects (you might not compute all elements of an array immediately)
16
Proxy pattern
• Problem: The application needs to operate on a distant
object
• Context: Need a placeholder for an object
– if the actual object is far away (on another computer or on a
disk), the placeholder should be in the local address space
• Solution: Define a Proxy class: this class will have the
same public interface as the real class, and when a
Proxy operation is called, the internals of a Proxy object
will arrange for the same function to be called on a real
object
– Proxy might send interprocess or intermachine messages
– Proxy might resurrect an object that has been written to disk
– Proxy might acquire special permissions to access a protected
real object
17
17
How to tell the difference?
• How can I tell which one I am using? Facade or Proxy?
– Both patterns are separating the implementation details from an
abstract interface
– But the intent is different for each pattern
• Facade: the application wants to use services of an
entire subsystem or package (usually tens or hundreds
of objects in the subsystem)
• Facade is also a common way to interface with non-OO
modules
• Proxy provides convenient access to an object – the
object might be on disk, in another process, on another
computer
18
18
Observer – a useful design technique
• Observer pattern always has at least 2 classes: Subject and
Observer
– TemperatureSensor – maintains information about the current
temperature
– TempReportWebPage – displays temperature value on a web page
• Each temperature value might appear on multiple pages
• Update each page when the TemperatureSensor changes state
page1: TempReportWebPage
temp: TemperatureSensor
report_URL : string
Update
page2: TempReportWebPage
call Update()
on each page
report_URL : string
Update
page3: TempReportWebPage
report_URL : string
Update
19
Observer is useful because there is often a separation between state and behavior in certain
designs. If an object’s state is changed, there may be several objects that expect to be
informed of that state change.
Note that in this case, the “action to be taken” on a state change of TemperatureSensor
usually belongs to the “observer” class (TempReportWebPage). The TemperatureSensor
class doesn’t know anything about web pages – TemperatureSensor only keeps a list of other
objects that depend on the state of the sensor.
(Note: In the example on this slide, the TemperatureSensor object is responsible for updating
multiple web pages. This is a common occurrence – you might have a system that records
temperatures from various reporting stations, but the screens to “view” the data might be
interested in different subsets of the reporting stations… all of the airports in a particular
state, all airports serving a metropolitan area, all airports that have more than 500 flights per
day, and so on.)
19
Simple class diagram for Observer
•
In the Observer Pattern:
there are links between two
kinds of concrete classes
– subjects and observers
•
•
Each observer object (such
as TempReportWebPage)
needs to register by calling
the Attach() operation on a
subject object (like
TemperatureSensor)
Each observer object will
have its Update() operation
called whenever its subject
changes state
ConcreteSubject observers
ConcreteObserver
subjectState
1
0..* observerState
GetState()
Update()
ModifyState()
Attach(Observer)
observerState =
Detach(Observer)
subject->GetState();
Notify()
for all o in observers {
o ->Update()
}
subjectState = newState;
Notify();
20
This is a simplified version of the “general picture” of the Observer pattern.
This pattern can be made more general by adding abstract classes… see the next page.
20
Complete class diagram for Observer
•
•
•
Observer is an abstract
interface that each
ConcreteObserver must
implement (must implement
an Update() function)
Observer objects still
register by calling the
Attach() operation on a
ConcreteSubject object
Each ConcreteObserver
object will have its Update()
operation called whenever
its ConcreteSubject
changes state
Subject
Attach(Observer)
Detach(Observer)
Notify()
observers
Observer
1
0..*
Update()
for all o in observers {
o ->Update()
}
ConcreteSubject
subjectState
GetState()
return subjectState
ConcreteObserver
observerState
Update()
observerState =
subject->GetState();
21
This is the “general picture” of the Observer pattern (from the Design Patterns book). It has
been generalized: the ConcreteSubject gets the “notification” functionality from its base
class Subject. Within the implementation of the ConcreteSubject class, there will be one or
more calls to the Subject::Notify() function – whenever the Subject determines that the
Observers need to be notified.
Observer is an “abstract class” – it defines the interface that all ConcreteObservers must
implement.
21
Why update the Observer objects?
•
•
In the Observer Pattern, there are some objects that “care about”
the state changes within other objects
The ConcreteObserver::Update() function might do these things:
– repaint a user interface
– check for exceeding thresholds
•
So the ConcreteObserver is a place to put operations that might
clutter up a domain class
– example: any “View” class
•
Or the ConcreteObserver is a place to put a command handler for a
user interface class
– button and menu handlers can be implemented as Observers of GUI
objects
22
22
Motivation for Observer
•
An early OO user interface architecture: Model-ViewController
– A model, is an object representing data (often data
about an object in the real world)
– A view is some form of visualization of the state of the
model.
– A controller offers facilities for the user to change the
state of the model (clicking on buttons, sliding a slider,
typing in a text box, etc.)
•
Some elements within the View observe the Model
– if the Model changes state, then the View may need to
update its objects and pass the changes to the user
interface implementation
•
The Model observes the Controller
– after the Controller accepts command input from the
user, it needs to notify the Model to update its state
View
A 5
Z
Model
event proc1
event proc2
Controller
23
Model-View-Controller (MVC) is a classic “separation of concerns” within a design. It is
possible to implement this with a single Model class, a single View class, and a single
Controller class, or with each part of the architecture implemented using a number of related
classes.
A good concise explanation of Model-View-Controller can be found on the OO Tips website:
http://ootips.org/mvc-pattern.html. See also http://c2.com/cgi/wiki?WhatsaControllerAnyway.
Note that there are some frameworks that implement a variation of MVC: Document-View.
This combines the Controller and the View into a single Document class. One popular
implementation of the Document-View architecture is the Microsoft Foundation Classes
(MFC).
23
Observer implementation
•
A Subject class will usually keep a linked list of pointers to its
registered Observers
– linked list: multiple View objects might observe the same Model, and
the linked list makes it easier when one of the middle objects wants to
Detach (to stop receiving notifications)
– each pointer in the list is of type Observer* (abstract type defining the
Update() operation), because the concrete types for different observers
is often quite different
•
Is this necessary?
– No. In some cases, you might know that all observers have the same
type. Then the list might declared to contain ConcreteObserver*
pointers.
– In fact, the linked list could be replaced by a fixed sized pointer table or
a dynamic sized array (std::vector in C++) – especially if the set of
observers is relatively static
24
24
Analysis patterns
• Analysis patterns are a set of patterns that are used in
doing the initial problem analysis:
– They help answer the question “what objects should I define in
my system?”
• The Quantity pattern is from the book Analysis Patterns
by Martin Fowler
– Recording measurements and manipulating results might be
error-prone
– Each value really should be recorded with its units:
• A Money object will have both a number and an identifier to say
which currency: [19.95, “US Dollars”]; [700, “Euros”]; [100, “Yuan”]
• Length and weight also need units: [100, “miles”]; [15.5, “kg”]
25
Analysis Patterns are just like Design Patterns – they are simple solutions to recurring
problems. Each Analysis Pattern has problem and a context, and it identifies certain common
solutions.
25
Justification for the Quantity pattern
• A frequent problem – someone tries to perform an invalid
operation on two different types of quantities:
– adding apples to oranges, people to money, dates to time
intervals
– conversion mistakes: adding dollars to euros, inches to feet
– performing an average of a mixed bag of objects (this should
never be legal)
• Using explicit units in the design makes it easier for
someone else to understand the software later
– what does this number mean??
26
26
Architecture patterns
• You can save a lot of design work by reusing some welltested architectures:
– Patterns of Software Architecture: the first book on “architecture
patterns”
• An example: Client-Dispatcher-Server
– Problem: You are designing a “video on demand” service
– Your customers can order different movies, and you have video
servers in the central office that can play the movies
– When requests come in, the system needs to decide which
server will handle each request
– There is a standard “division of responsibilities” in the design
27
27
Client-Dispatcher-Server example
•
initial
stimulus
send_request(s)
A Customer wants a particular
Service, so
Customer::send_request() is
c: Customer
1: receive_request(c)
•
called.
This will invoke the Service (it calls the
Service::receive_request()
s: Service
1.1: sn = obtain_channel(c)
•
:ServiceNode
Assigner
1.1.2: add
NodeUser(c)
1.1.1*: get
Capacity_and_Load()
•
function for a specific Service object).
The Service object wants either to
create a new ServiceNode or (more
likely) to assign an existing
ServiceNode to the Customer.
The ServiceNodeAssigner class
makes the decision about which
ServiceNode to assign and configure.
sn: ServiceNode
1.1.4: configure
Service()
28
28
Client-Dispatcher-Server example
• This design uses the “Client-Dispatcher-Server” pattern:
– three classes share responsibility for managing communication
• the Server provides a set of services, but it initially registers itself
with the Dispatcher
• the Client will invoke the services provided by one or more
Servers, but it will contact the Dispatcher each time it needs a
service (to help it find the right Server)
• the Dispatcher tracks the Servers and responds to the Clients that
are trying to locate a service
• The design is relatively clean:
– Clients have a single logical point of contact
– the Servers don’t need to know anything about the assignment
policy
29
29
Enterprise Architecture Patterns
• Martin Fowler’s excellent book: Patterns of Enterprise
Application Architecture
– Enterprise Applications – software that uses databases
(concurrent access to persistent data)
– How do you write a Java application to manipulate and display
data from a database?
– How can you make it easier to modify the application to meet
new needs without breaking the existing code?
• Some answers:
– use layers, build facades, define proxies, encapsulate data
records, and encapsulate transactions
30
30
Example enterprise patterns
• How do I control the formatting of my Web pages?
– If I want to edit the page and put hooks in for dynamic data? – use
Template View (HTML page with markers for varying information)
– The page is a transform of domain data? – use Transform View
– If I need to make general “look-and-feel” changes across my web site?
– use Two Step View
– Multiple appearances for the same logical screen? – use Two Step
View
• How do I interact with the database?
– If I have a domain model that corresponds to the database tables? –
use Active Record (encapsulating database access and domain logic
into one class)
– If I have a rich domain model? – use Data Mapper
– If I’m using a Table Module? – use Table Data Gateway
31
These examples are taken from the “Cheat Sheet” in the back cover of the Martin Fowler
Patterns of Enterprise Application Architecture book.
31
Summary
• Design Patterns: an important set of object oriented
design concepts
– these patterns are useful in many applications
– every pattern has a documented set of “Consequences”
• Some useful sources on design patterns:
–
–
–
–
http://hillside.net/patterns/onlinepatterncatalog.htm
http://www.martinfowler.com/articles/enterprisePatterns.html
http://java.sun.com/blueprints/corej2eepatterns
http://www.headfirstlabs.com/books/hfdp/
32
32