Title: More on Design Patterns
1More on Design Patterns
- CS320 Fundamentals of Software Engineering
2Review Patterns
- Principles and idioms codified in a structured
format describing the problem, solution, and
given a name are called patterns.
3Classification of Patterns
4Designing for Change Causes for Redesign (I)
- Creating an object by specifying a class
explicitly - Commits to a particular implementation instead of
an interface - Can complicate future changes
- Create objects indirectly
- Patterns Abstract Factory, Factory Method,
Prototype - Dependence on specific operations
- Commits to one way of satisfying a request
- Compile-time and runtime modifications to request
handling can be simplified by avoiding hard-coded
requests - Patterns Chain of Responsibility, Command
5Causes for Redesign (II)
- Dependence on hardware and software platform
- External OS-APIs vary
- Design system to limit platform dependencies
- Patterns Abstract Factory, Bridge
- Dependence on object representations or
implementations - Clients that know how an object is represented,
stored, located, or implemented might need to be
changed when object changes - Hide information from clients to avoid cascading
changes - Patterns Abstract factory, Bridge, Memento, Proxy
6Causes for Redesign (III)
- Algorithmic dependencies
- Algorithms are often extended, optimized, and
replaced during development and reuses - Algorithms that are likely to change should be
isolated - Patterns Builder, Iterator, Strategy, Template
Method, Visitor - Tight coupling
- Leads to monolithic systems
- Tightly coupled classes are hard to reuse in
isolation - Patterns Abstract Factory, Bridge, Chain of
Responsibility, Command, Facade, Mediator,
Observer
7Causes for Redesign (IV)
- Extending functionality by subclassing
- Requires in-depth understanding of the parent
class - Overriding one operation might require overriding
another - Can lead to an explosion of classes (for simple
extensions) - Patterns Bridge, Chain of Responsibility,
Composite, Decorator, Observer, Strategy - Inability to alter classes conveniently
- Sources not available
- Change might require modifying lots of existing
classes - Patterns Adapter, Decorator, Visitor
8BUILDER(Object Creational)
- Intent
- Separate the construction of a complex object
from its representation so that the same
construction process can create different
representations - Motivation
- RTF reader should be able to convert RTF to many
text format - Adding new conversions without modifying the
reader should be easy - Solution
- Configure RTFReader class with a TextConverter
object - Subclasses of TextConverter specialize in
different conversions and formats - TextWidgetConverter will produce a complex UI
object and lets the user see and edit the text
9BUILDERMotivation
RTFReader
builders
TextConverter
ParseRTF()
ConvertCharacter(char) ConvertFontChange(Font) Con
vertParagraph()
while(tget the next token) switch
t.Type CHAR builder-gtConvertCharacter(t.Char)
FONT builder-gtConventFontCharnge(t.Font)PARA
Builder-gtConventParagraph()
ASCIIConverter
TextConverter
TextWidgestConverter
ConvertCharacter(char) GetASCIIText()
ConvertCharacter(char) ConvertFontChange(Font) Con
vertParagraph() GetTeXText()
ConvertCharacter(char) ConvertFontChange(Font) Con
vertParagraph() GetTextWidget()
TextWidget
TeXText
ASCIIText
10Applicability
- Use the Builder pattern when
- The algorithm for creating a complex object
should be independent of the parts that make up
the object and how they are assembled - The construction process must allow different
representations for the object that is constructed
11BUILDERStructure
builders
Director
Builder
BuildPart ()
Construct ()
for all objects in structure
builder-gtBuildPart ()
ConcreteBuilder
Product
BuildPart ()GetResult ()
12Builder - Collaborations
- Client creates Director object and configures it
with the desired Builder object - Director notifies Builder whenever a part of the
product should be built - Builder handles requests from the Director and
adds parts to the product - Client retrieves the product from the Builder
13BUILDERCollaborations
aDirector
aClient
aConcreteBuilder
new ConcreteBuilder
new Director (aConcreteBuilder)
BuildPart A ()
BuilPart B ()
BuildPart C ()
GetResult ()
14The Bridge Pattern intention
- The intention of the Bridge pattern is to
Decouple an abstraction from its implementation,
so that the two can vary independently.
15The Bridge Pattern Example
- Suppose I have been given the task of writing a
program that will draw rectangles with either of
two drawing programs. I have been told that when
I instantiate a rectangle, I will know whether I
should use drawing program 1 (DP1) or drawing
program 2 (DP2). - The rectangles are defined as two pairs of points
(x1,y2)
(x2,y2)
(x1,y1)
(x2,y1)
16The Bridge Pattern Example (2)
- The differences between the drawing programs
- Used to draw a lineDP1 draw_a_line(x1,y1,x2,y2)
DP2 drawline(x1,x2,y1,y2) - Used to draw a circleDP1 draw_a_circle(x,y,r)D
P2 drawcircle(x,y,r)
17The Bridge Pattern Example (3)
- My customer told me that the collection (the
client of the rectangles) does not want to worry
about what type of drawing program it should use.
It occurs to me that since the rectangles are
told what drawing program to use when
instantiated, I can have two different kinds of
rectangle objects one that uses DP1 and one that
uses DP2.(see next slide)
18The Bridge Pattern Example (4)
Rectangle draw() drawLine()
Client
V1Rectangle drawLine()
V2Rectangle drawLine()
DP1 draw_a_line()
DP2 drawline()
19The Bridge Pattern Example (5)
- By having an abstract class Rectangle, I take
advantage of the fact that the only difference
between the different types of Rectangles are how
they implement the drawLine method. - Now, suppose that after completing this code, one
of the inevitable three (death, taxes, and
changing requirements) comes my way. I am asked
to support another type of Shape, this time a
circle. I am also given the mandate that the
collection object does not want to know the
difference between Rectangles and Circles. - For a first design see next slide.
20 Bridge Pattern Trial Design
Shape draw()
Client
Rectangle draw() drawLine()
Circle draw() drawCircle()
V1Rectangle drawLine()
V2Rectangle drawLine()
V1Circle drawCircle()
V2Circle drawCircle()
DP1 draw_a_line() draw_a_circle()
DP2 drawline() drawcircle()
21The Bridge Pattern Example (7)
- To understand this design, lets walk through an
example. Consider what the draw method of a
V1Rectangle does. - Rectangles draw method is the same as before
(calling drawLine four times as needed). - drawLine is implemented by calling DP1s
draw_a_line.
22The Bridge Pattern Example (8)
Client
myRectangleV1Rectangle
DP1
draw()
drawLine(x1,y1,x2,y1)
draw_a_line(x1,y1,x2,y1)
drawLine(x2,y1,x2,y2)
draw_a_line(x2,y1,x2,y2)
drawLine(x2,y2,x1,y2)
draw_a_line(x2,y2,x1,y2)
drawLine(x1,y2,x1,y1)
draw_a_line(x1,y2,x1,y2)
23The Bridge Pattern Example (9)
- Even though the class diagram makes it look like
there are many objects, in reality, I am only
dealing with three objects - The client using the rectangle
- The V1Rectangle object
- The drawing program DP1.
24The Bridge Pattern Example (10)
- Unfortunately, this approach introduces new
problems. Remember the class Diagram and pay
attention to the third row of classes. Consider
the following - The classes in this row represent the 4 specific
types of Shapes that I have. - What if I get another drawing program, that is,
another variation in implementation? I will have
6 different kinds of Shapes (2 Shape concepts
times 3 drawing programs). - What if I then get another type of Shape, another
variation in concept? I will have 9 different
types of Shapes.
25The Bridge Pattern Example (11)
- The class explosion problem arises because in
this solution, the abstraction (the kinds of
Shapes) and the implementation (the drawing
programs) are tightly coupled. Each type of
shape must know what type of drawing program it
is using. - I need a way to separate the variations in
abstraction from the variations in
implementation, so that the number of classes
only grows linearly.
26The Bridge Pattern Example (12)
- This is exactly the intent of the Bridge
patternto de-couple an abstraction from its
implementation so that the two can vary
independently.
Abstraction 1 Abstraction 2 Abstraction 3 ...
Implementation A Implementation B Implementation
C ...
27The Bridge Pattern Example (13)
- Looking at the Class Diagram, ask yourself what
else is poor about this design. - Does there appear to be redundancy?
- Would you say things have high cohesion or low
cohesion? - Are things tightly or loosely coupled?
- Would you want to have to maintain this code?
- The overuse of inheritance
- Use inheritance selectively to realize its power.
Move variations into used or owned objects
(composition). - Look for alternatives in initial design
- Very important (good practice), but avoid
paralysis by analysis
28The Bridge Pattern Derivation
- Two basis strategies
- Find what varies and encapsulate it
- Often favor composition over inheritance
- What varies?
Drawing drawLine() drawCircle()
Shape draw
29Th Bridge Pattern Derivation (2)
- The abstract classes Shape and Drawing
encapsulate the specific variations
Drawing drawLine() drawCircle()
Shape draw()
Rectangle draw()
Circle draw()
V1Drawing drawLine() drawCircle()
V2Drawing drawLine() drawCircle()
30The Bridge Pattern Derivation (3)
- Use composition instead of inheritance
- Drawing uses Shape (awkward, since then Drawing
has to know things of Shape) - Shape uses Drawing (better choice)
Drawing drawLine() drawCircle()
Shape draw()
Rectangle draw()
Circle draw()
V1Drawing drawLine() drawCircle()
V2Drawing drawLine() drawCircle()
31The Bridge Pattern Derivation (4)
- Addition of further dependencies
Drawing drawLine() drawCircle()
Shape draw() drawLine() drawCircle()
V1Drawing drawLine() drawCircle()
V2Drawing drawLine() drawCircle()
Rectangle draw()
Circle draw()
DP1 draw_a_line() draw_a_circle()
DP2 drawline() drawcircle()
32The Bridge Pattern Derivation (5)
- Separation of the Shape abstraction from the
Drawing implementation
Drawing drawLine() drawCircle()
Shape draw() drawLine() drawCircle()
V1Drawing drawLine() drawCircle()
V2Drawing drawLine() drawCircle()
Rectangle draw()
Circle draw()
DP1 draw_a_line() draw_a_circle()
DP2 drawline() drawcircle()
Shape abstraction
Drawing implementation
33The Bridge Pattern Derivation (6)
- Note that the solution integrates the Adapter
pattern with the Bridge pattern. This is caused
by the fact that the interfaces of DP1 and DP2
have to be adapted to the interface needed. - While it is very common to see the Adapter
pattern incorporated into the Bridge pattern, the
Adapter pattern is not part of the Bridge pattern.
34The Bridge Pattern Key Features
- Intent Decouple a set of implementations from
the set of objects using them. - Problem The derivations of an abstract class
must use multiple implementations without
causing an explosion in the number of
classes. - Solution Define an interface for all
implementations to use and have the
derivations of the abstract class use it
35The Bridge Pattern Key Features (2)
- Participants and The Abstraction defines
the Collaborators interface for the
objects being implemented. The
Implementor defines the interface for
the specific implementation
classes. Classes derived from the
Abstraction use classes derived from the
Implementor without knowing which
particular ConcreteImplementor is in use.
36The Bridge Pattern Key Features (3)
- Consequences The decoupling of the
implementations from the objects that use
them increases extensibility.
Client objects are not aware of
implementation issues. - Implementation Encapsulate the implementations in
an abstract class. Contain a handle to it in
the base class of the abstraction being
implemented. NB In Java use interfaces
(instead of abstract classes) for
the implementation.
37The Bridge Pattern Key Features (4)
- Standard, simplified view of the Bridge Pattern
Abstraction operation()
Implementor OperationImp()
imp-gtOperationImp()
RefinedAbstraction
ConcreteImplementorA OperationImp()
ConcreteImplementorA OperationImp()
38The Bridge Pattern Discussion
- OO principles used
- Objects are responsible for itself
- Abstract classes
- Encapsulation via an abstract class
- One rule, one place
- the abstract class often has the methods that
actually use the implementation objects. The
derivations of the abstract class call these
methods.
39Pattern Iterator
- objects that traverse collections
40Iterator pattern
- iterator an object that provides a standard way
to examine all elements of any collection - uniform interface for traversing many different
data structures without exposing their
implementations - supports concurrent iteration and element removal
- removes need to know about internal structure of
collection or different methods to access data
from different collections
41Iterator interfaces in Java
- public interface java.util.Iterator
- public boolean hasNext()
- public Object next()
- public void remove()
-
- public interface java.util.Collection
- ... // List, Set extend Collection
- public Iterator iterator()
-
- public interface java.util.Map
- ...
- public Set keySet() // keys,values are
Collections - public Collection values() // (can call
iterator() on them)
42Iterators in Java
- all Java collections have a method iterator that
returns an iterator for the elements of the
collection - can be used to look through the elements of any
kind of collection (an alternative to for loop) - List list new ArrayList()
- ... add some elements ...
- for (Iterator itr list.iterator()
itr.hasNext()) - BankAccount ba (BankAccount)itr.next()
- System.out.println(ba)
set.iterator() map.keySet().iterator() map.values(
).iterator()
43Adding your own Iterators
- when implementing your own collections, it can be
very convenient to use Iterators - discouraged (has nonstandard interface)
- public class PlayerList
- public int getNumPlayers() ...
- public boolean empty() ...
- public Player getPlayer(int n) ...
-
- preferred
- public class PlayerList
- public Iterator iterator() ...
- public int size() ...
- public boolean isEmpty() ...
-
44MVC
- General application structure.
- Design patterns
- Model View Controller,
- Observer/Observable.
- Implementation problems and strategies.
45General Structure
An application generally consists of
46But What Does It Look Like?
47Role of the User Interface
- 1. To represent the domain model to the user
- 2. To allow the user to control the model.
- NOT to be part of the model.
48User Interface Requirements
- Represent objects of interest.
- Provide alternative representations.
- Allow control via different mechanisms.
- Implies that the user interface must be
loosely-coupled to the application model.
49Potential Problems
- How does the controller part of the interface
know when an interaction is requested?
(Event-driven programming) - What happens if the model is (sometimes) CPU
intensive? (Threads)
50Decoupling
- A flexible, loosely-coupled arrangement is
appropriate for this separation of application
model and user interface. - The Model-View-Controller design pattern derived
from Smalltalk is a useful construct.
51Model-View-Controller
- A design pattern consisting of a triad of
classes - Model - the application object,
- View - the screen presentation of the object,
- Controller - defines the way the user interface
reacts to input. - Classes are decoupled by defining a subscribe/
notify protocol. - Multiple views and controllers can be defined.
52Model-View-Controller
Relies on an Observer mechanism (another design
pattern) to connect the classes. A View must
ensure that it accurately and completely reflects
the current state of the Model. The Model
notifies all Views that depend on it. The
Controller modifies the state of the object
through its programming interface in response to
user actions.
53MVC (cont.)
1
Model Register(Observer) Unregister(Observer) Not
ifyAll()
Observer virtual OnUpdate()
for all o in observers o.OnUpdate()
Controller
View virtual OnUpdate()
54Advantages
- Multiple views can be added to an object.
- Multiple control mechanisms can be implemented.
- Presentation and control of the model can be
changed without rewriting the model itself.
55Why Is This Useful?
- It allows application-specific code to be
localized. - It allows the view to be different from the
model. - It allows multiple differing views of the same
model. - It allows the mechanism for control to be
separate from both the view and the model.
56MVC in Java
- Java.util.Observable - a base class that
incorporates the observer mechanism. - Java.util.Observer - an interface that can be
implemented which notices when objects that it is
observing change state. - The Swing user interface classes use the
Model-View-Controller pattern as a basis.
57Implementation in Java
- Model classes extend Observable.
- View classes extend Applet and implement
Observer. - Each View must
- implement the update() method and
- be added to the Model instance via addObserver().
- When the Model calls notifyObservers(), the
update() method of all observing Views will be
called.
58Implementation in C
- No explicit code for Observer/Observable
mechanisms. - View and Controller interact with the Model via
normal Get and Set functions. - Some libraries allow callback functions, e.g.
OpenGL and qt. - For an alternative view, see outerface
(http//www.outerface.com/).
59Summary
- Flexible decoupling of user interface and
application is essential. - Model-View-Controller is a useful design pattern.
- Java is amenable to MVC implementation through
the Observer/Observable classes. - Complete decoupling is not always possible, or
may lead to inelegant code.