320 likes | 485 Views
Design Patterns. MSO 08/09, WP. Design pattern. Client : “ I want you to make a painting of our family .” Painter : “ I can do various styles: realistic, impressionistic, abstract. Which one do you want ?” Design pattern : pattern for solving a repeating problem.
E N D
Design Patterns MSO 08/09, WP
Design pattern • Client : “I want you to make a painting of our family.” • Painter : “I can do various styles: realistic, impressionistic, abstract.Which one do you want ?” • Design pattern : pattern for solving a repeating problem. • Rigid, as in “map” in Haskell • Loose, as we will discuss here
Design patterns in software engineering • OO is one hand very expressive! • But it also overwhelms us with lots of options to solve the same problem. • Novice developer: • have to make the choice • may make a wrong choice • will re-invent lots of solutions • Expert developer: • know lots of solution patterns from • experience • re-apply patterns to solve next • problem
Software architect’s bible Documenting 23 useful design patterns, learned from experience. “Gang of Four” (GOF)
Our plan today • Discuss some of GOF’s patterns. • There is 1x assignment to help you understand them. • Unfortunately, we have no time for actual programming I recommend (this is your own activity, not assignment): • write a proto-implementation of your solution in Java • buy GOF’s book, and apply it when you have to work on your Java projects.
Where are we ? • UML is very generic, you can use it to model things at various levels Person Patient Doctor Patient Doctor 0..* 0..* PatientState Implementation Design
Example • Problem: I need a class “God” that can only have 1 instance. • Motivation:A God-object can do powerful things; one God-object is sufficient.For security I need to guarantee that there is indeed only one God-object, implying that it would be sufficient for me to monitor this single God. • Solution ??
Singleton Pattern Solution class God { … // fields and other methods here private God() { ... } ; static private God i = new God() ; public static God getInstance() { return i ; } } • God • i: God • God() • + God getInstance() pattern ! • Singleton • i: Singleton • Singleton() • + Singleton getInstance()
Problem: salary administrator • A salary administrator is responsible for calculating yearly bonus of employees.The algorithm to calculate this differs depending on an employee’s function. • Direct solution: List<Employee> elist Class SalaryAdminstrator { …calcBonus() { for (Employee e : elist) if (e.function == PROFESSOR) e.bonus = 0 else if (e.function == LECTURER) e.bonus = 1.9 * e.salary else e.bonus = 1.1 * e.salary } …
Solution : exploit subclassing Class SalaryAdminstrator { …calcBonus() { } … salary bonus for (Employee e : elist) e.calcBonus() Employee … + calcBonus() calcBonus() { bonus = 0 } calcBonus() { bonus = 1.9 * salary } Secretary … + calcBonus() Lecturer … + calcBonus() Professor … + calcBonus()
Strategy Pattern Client Strategy … + calc() 0..* 1 StrategyC … + calc() StrategyB … + calc() StrategyA … + calc()
What are the main guidelines ?? • Modularity ! • ExtensibilityWe should be able to extend a software without ‘changing’ the existing code. • ReusabilityWe should avoid replicating the same functionalities error prone, and higher maintenance cost. • On the other hand, balance this with coupling/dependencyIncreasing the coupling also makes a software more error prone, and increases maintenance cost.
Problem : tables • A program wants to calculate the number of cells that a given table has.A table consists of rows. Each row consists of cells, and may be of different length. • Direct solution : Table class App { ...numCells(Table t) {inti = 0 ; for (Row r : t.rows) for (Cell c : r.cells) i++ ; return i ; } rows 0..* Row cells 0..* Cell
A nicer solution + numOfCells() : int 0..* Component children Cell Composite numOfCells() { return 1} numOfCells() { int k = 0 ; for (Component c : children) k = k + c.numOfCells() ; return k ; } Table Row Now "App" can just look like this : class App { ...numCells(Component c) { return c.numOfCells() ; } ...
"Composite" Pattern Component+ operation() 0..* Client children Composite + operation() + add(Component c) + remove(Component c) + getChildren() : List<Component> Cell + operation()
Pattern classification (GoF) • Creational patterns • Singleton • Factory Method, Abstract Factory • Structural patterns • Composite • Bridge, Proxy • Behavioral patterns • Strategy, Observer, Template Method, Visitor (?)
A D B C A B C D Problem: live charts This picture is taken from A.P. Mathur, slides "Design Patterns", CS 406 Software Engineering I, Purdue Univ. 2001. A=10 B=40 C=30 D=20 controller • We want to display this data in various charts. • There is a controller active that works on the data. • We want the change to be mirrored “live” in the charts.
Let’s look at similar but simpler problem Celcius display Fahrenheit display class Heater { int temp ; Thermometer ct ; Thermometer ft ; ... void change(int d) { temp = temp + d ; ct.update(temp) ; ft.update(9*temp/5 + 32) } } Heater Controller
Solution Make it so that we can add/remove thermometers. class Heater { private int temp ; public List<Thermometer> ts ; ... void change(int d) { temp = temp + d ;notify() ; } public void notify() { for (Thermometer t : ts) t.update(this) } } We can add/remove Thermometer to this list. Here is where we will put the temperature conversion.
Solution Thermometer - temp : int + update(Heater) Subject + add(Thermometer) + remove(Thermometer) + notify() oberservers 0..* FahThermometer CelciusThermometer Heater - temp : int update(h) { temp = h.temp } update(h) { temp = 9*h.temp/5 + 32 }
Observer Pattern Subject + add(Thermometer) + remove(Thermometer) + notify() Observer + update(Subject) oberservers 0..* ConcreteObs2 ConcreteObs1 ConcreteSubject
MVC • Model View Controller : a variant of Observer Pattern (Model) (View) usually GUI Subject + add(Thermometer) + remove(Thermometer) + notify() Observer + update(Subject) 0..* Controller
Problem • I need different kinds of thermometers, but the decision can only be done at the run time. class Panel { Themometer t ; ... public Panel(Class tkind) { if (tkind == CelciusThermometer.class) t = new CelciusThermometer() else if (tkind == FarThermometer.class) t = new FarThermometer() else ... // error ! ... } }
Solution : Factory Method pattern public Panel(Class tkind) { t = ThermometerCreator.factoryMethod(tkind) ... } ThermometerCreator + factoryMethod (Class) : Thermometer static public factoryMethod(Class tkind) { if (tkind == CelciusThermometer.class) return new CelciusThermometer() else if (tkind == FarThermometer.class) return new FarThermometer() else ... // error ! }
Problem if (s.isEmpty()) return null ; m = s.get(0) ;for (k : s) if (k.compareTo(m) > 0) m=k return m ; • I need a method that calculates the max element of two lists. class MyClass { private Comparable max(List<Comparable> s) ... Comparable max2(List<Comparable> s, List<Comparable> t) { m1 = max(s) ; m2 = max(t) ; if (m1==null && m2==null) return null ; if (m1==null) return m2 ; if (m2==null) return m1 ; if (m1.compareTo(m2) > 0) return m1 else m2 ; } }
Solution abstract class TemplateSolution { abstract Comparable max(List<Comparable> s) Comparable max2(List<Comparable> s, List<Comparable> t) { m1 = max(s) ; m2 = max(t) ; ... // as before } Solution1 max(s) { // as before } TemplateSolution # max + max2 Solution2 max(s) { return s.get(0) }
Template Method pattern ConcreteSolution1 TemplateSolution # operation1(...) # operation2(...)... + templateMethod(...) ConcreteSolution2 • The template method specifies an algorithm over the operations. • Each concrete solution implements or overrides the operations. • Keep in mind that this also increases the coupling if these operations may only be used in the way they're used by the template method.
Folding over a structure • I want to write methods to: • count the sum of all cells in a component • return me the cell with the max value children Component 0..* Cellvalue : int Composite
Direct solution & Visitor Pattern • We have to keep writing the recursion over the structure of Component! • Can't we get a fold-like solution here?? • Yes, with the Visitor Pattern. sumCell (Component u) { if (u instanceof Cell) return u.value ; Composite u_ = (Composite) u ;int k = 0 ; for (Component v : u_.children) k = k + sumCell(v) ; return k ; }
Home work • Self-study these patterns (see online Resource) : • Few more simple patterns: bridge, proxy, abstract factory • The visitor pattern (advanced)