OO Design Patterns
CSI323 Lecture
What is a Design Pattern?
A design pattern
abstracts a recurring design structure comprises class and/or object
dependencies, structures, interactions, or conventions
distills design experience
Re-use
Code re-use
Dont reinvent the wheel Requires clean, elegant, understandable, general, stable code leverage previous work
Design re-use
Dont reinvent the wheel Requires a precise understanding of common, recurring designs leverage previous work
What is a Design Pattern?
A pattern is a named problem/solution pair that can be applied in new contexts, with advice on how to apply it in novel situations. Patterns provide guidance for how responsibilities should be assigned to objects, given a specific category of problem. Patterns typically do not contain new ideas. Patterns attempts to codify existing knowledge, idioms and principles.
What is a Design Pattern? (contd)
There are four essential elements of a pattern. Pattern name Problem describes when to apply the pattern. Solution describes the elements that make up the design. Consequences are the results and trade-offs of applying the pattern.
Usually descriptions of communicating objects and classes that are customized to solve a general design problem in a particular context.
How Design Patterns Solve Design Problems
The hard part of OO design is decomposing a system into objects. Many objects in a design come from the analysis model. OO designs end-up with classes that dont have any counterparts in the real world. Design patterns help you identify less obvious abstractions.
A List of Design Patterns
Expert Creator Controller Mediator Decorator Prototype Object Pool Faade Observer Builder Adapter Command Abstract Factory Proxy Iterator
Expert
Most general purpose responsibility assignment principle? Assign a responsibility to the information expert the class that has the information necessary to fulfill the responsibility.
What class should be responsible for knowing a resource, given a call number?
Catalog is an information expert on finding and returning a resource, based on a call number. It logically contains all of them.
borrowResource(callNum)
1 : r := resource(callNum) : Resource :Library :Catalog
by Expert
Expert (contd)
Related to another fundamental responsibility assignment strategy which is central to object-oriented design:
Do it Myself (Coad) A Book calculated its own due date
Closely related to/Also known as:
Put services with attributes (Coad) That which knows, does it (Sieve)
Creator
Who should create an instance of a particular class? Consider assigning Class B the responsibility to create an instance of class A if one of the following is true:
B contains A B aggregates A B records A B closely uses A
B is the creator of A instances.
Creator
by Creator makeBook(title)
1 : create(title) :Catalog :Book
Controller
A controller is a class that handles a system event message. What class should handle a system event message? Assign the responsibility for handling a system operation message to one of these choices:
The business or overall organization (a faade controller). The overall system (a faade controller). An animate thing in the domain that would perform the work (a role controller). An artificial class representing the use case (a use case controller).
Controller - Facades
Facades are covers. Intent a class that (in some way) represents an overall cover.
by Controller
borrowResource(callNum) :Library
Controller Facades (contd)
Other facades? a class representing the system, e.g.,
The software information system. The device that includes a computer and software (e.g., ATM) Etc.
borrowResource(callNum) :Library
borrowResource(callNum) :LibInfoSystem
Observer design patterns
Behavioral Pattern one-to-many dependency model, so that when one object changes state, all its dependents are notified and updated automatically without coupling the notifying object to the objects that are notified. Example: Button expose a clicked event that encapsulate click state, thus publish himself as an observable. Clients that are interested in this event register to it, thus becomes observers. Observer and observable are bonded in a contract and can be completely loosely coupled from one another.
Singleton design pattern
Creational pattern ensure that a class has only one instance, and to provide a global point of access to it Example: Class SomeClass { static SomeClass singleTonInstance = null; static SomeClass GetInstance() { if(singleTonInstance == null) singleTonInstance = new SomeClass() return singleTonInstance; } }
Factory design patterns (abstract\method\Lightweight)
Creational pattern Can be given to client (abstract), pass construction parameters or read creation types from configuration or system environment Can use object pool (Lightweight)
Factory design pattern - example
abstract class GUIFactory { public static GUIFactory getFactory() { int sys = readFromConfigFile("OS_TYPE"); return sys == 0 ? new WinFactory() : new OSXFactory(); } public abstract Button createButton(); } class WinFactory:GUIFactory { public override Button createButton() { return new WinButton(); } } class MacFactory:GUIFactory { public override Button createButton(){ return new MacButton(); } } abstract class Button { public string caption; public abstract void paint(); }
Factory design pattern - example
class WinButton:Button { public override void paint() { // paint a button with Win API} } class MacButton:Button { public override void paint() { // paint a button Mac style } } class Application { static void Main(string[] args) { GUIFactory aFactory = GUIFactory.getFactory(); Button aButton = aFactory.createButton(); aButton.caption = "Play"; aButton.paint(); } }
Faade design pattern
Structural Pattern Provide a unified interface to a set of interfaces in a subsystem without damaging the genric form of the sub system.
Decorator design pattern
Structural Pattern Avoid excessive sub-classing and gain run time flexibility Example: Java.IO package
BufferedReader br = new BufferedReader( new InputStreamReader( new FileInputStream(inFile)));
All derives from abstract io.Reader
Strategy design pattern
Behavioral Pattern defines a family of interchangeable encapsulated algorithms that receives the same input type and provides the same output type in different manners that can be determined in run-time. static void Main( { SortedList studentRecords = new SortedList(); studentRecords.Add("Samual"); studentRecords.Add("Jimmy"); studentRecords.Add("Sandra"); studentRecords.SetSortStrategy(new QuickSort()); studentRecords.Sort(); studentRecords.SetSortStrategy(new ShellSort()); studentRecords.Sort(); }
Strategy design pattern - example
abstract class SortStrategy { public abstract void Sort(ArrayList list) } class QuickSort : SortStrategy { public override void Sort(ArrayList list) { list.Sort(); // Default is Quicksort } } class ShellSort : SortStrategy { public override void Sort(ArrayList list) { //list.ShellSort(); not-implemented } }
Strategy design pattern - example
class SortedList { private ArrayList list = new ArrayList(); private SortStrategy sortstrategy; public void SetSortStrategy(SortStrategy sortstrategy) { this.sortstrategy = sortstrategy; } public void Add(string name) { list.Add(name); } public void Sort() { sortstrategy.Sort(list); } }
Consumer/Producer
Concurrency Pattern This design pattern coordinates the concurrent production and consumption of information among producer and consumer objects that are working on different threads. This pattern is used with some type of semaphore
Consumer/Producer - example
static AutoResetEvent eventProducerDone = new AutoResetEvent(false); static AutoResetEvent eventConsumerDone = new AutoResetEvent(false); static int currentNum = 0; static void produce(object stateInfo) { eventProducerDone.Set(); while (true) { //wait for the consumer eventConsumerDone.WaitOne(); currentNum++; eventProducerDone.Set(); } }
Consumer/Producer - example
static void Main(string[] args) { ThreadPool.QueueUserWorkItem(new WaitCallback(produce)); for (int i = 0; i < 20; i++) { eventProducerDone.WaitOne(); System.Diagnostics.Debug.WriteLine(currentNum); eventConsumerDone.Set(); } }
Model View Controller
The Model-View-Controller (MVC) pattern separates the modeling of the domain, the presentation, and the actions based on user input into three separate classes The controller changes the model The View Listens to Model Changed events and update itself Recursive MVC
Subject-observer
[from Vlissides]
Subject-observer (cont.)
1 * Observer OnUpdate()
Subject Register(Observer) Unregister(Observer) NotifyAll()
for all o in observers { o.OnUpdate() }
Subject-observer (cont.)
Subject Register(Observer) Unregister(Observer) NotifyAll() 1
Observer virtual OnUpdate()
for all o in observers { o.OnUpdate() }
ConcreteSubject
ConcreteObserver
virtual OnUpdate()
Model / view / controller (MVC)
View Controller (displays data) (mediates) (holds data)
Create()
{ Model m; Controller c(&m); View v(&c);
Model
calls Register()
Create()
Main View Create()
Controller
Model
Register()
MVC (cont.)
Subject Register(Observer) Unregister(Observer) NotifyAll() 1
Observer virtual OnUpdate()
for all o in observers { o.OnUpdate() }
Controller
View
virtual OnUpdate()
MVC (cont.)
class Observer { protected: virtual void OnUpdate(MsgId message_id) = 0; }; class Subject { public: enum MsgId {}; void RegisterObserver(Observer* obs); virtual void NotifyAllObservers(MsgId message_id) { for (int i=0 ; i<m_observers.size() ; i++) { m_observers[i]->OnUpdate(message_id); } } private: std::vector<Observer*> m_observers; };
MVC (cont.)
class Controller : public Subject { Controller(Data* d) : m_data(d) {} const Data* GetData() const; void AddSphere(const Sphere& s) { m_data->AddSphere(s); NotifyAllObservers(ADD_SPHERE); } private: Data* m_data; };
MVC (cont.)
class MainWnd : public Observer { public: MainWnd(Controller* c) : m_controller(c) { c.Register(this); } virtual void OnUpdate(int message_id) { switch (message_id) { case Subject::ADD_SPHERE: ... } } private: Controller* m_controller; };
Adapter
You have
legacy code current client
Adapter changes interface of legacy code so client can use it Adapter fills the gap b/w two interfaces No changes needed for either
legacy code, or client
Adapter (cont.)
class NewTime { public: int GetTime() { return m_oldtime.get_time() * 1000 + 8; } private: OldTime m_oldtime; };
Command
You have commands that need to be
executed, undone, or queued
Command design pattern separates
Receiver from Invoker from Commands
All commands derive from Command and implement do(), undo(), and redo()
Facade
You
have a set of related classes want to shield the rest of the system from these details
Facade provides a simplified interface Encapsulates a subsystem
Composite
You want uniformly to treat
items (atomic elements), and groups (containing items or other groups)
Composite interface specifies operations that are shared between items and groups Examples: hierarchy of files and directories, groups of drawable elements
Composite (cont.)
Composite
Item
Group
Proxy
You want to
delay expensive computations, use memory only when needed, or check access before loading an object into memory
Proxy
has same interface as Real object stores subset of attributes does lazy evaluation
Strategy
You want to
use different algorithms depending upon the context avoid having to change the context or client
Strategy
decouples interface from implementation shields client from implementations Context is not aware which strategy is being used; Client configures the Context strategies can be substituted at runtime example: interface to wired and wireless networks
Strategy (cont.)
Client Policy
Context
Strategy
Concrete StrategyA
Concrete StrategyB
Bridge
You
have several different implementations need to choose one, possibly at run time
Bridge
decouples interface from implementation shields client from implementations Abstraction creates and initializes the ConcreteImplementations Example: stub code, slow code, optimized code
Bridge (cont.)
Client
Abstraction
Implementor
Concrete ImplementorA
Concrete ImplementorB
Refined Abstraction
Design pattern space
[from Vlissides]
END