1 / 41

Design Pattern 2 [FF 6 – 11]

Design Pattern 2 [FF 6 – 11]. Remember Starbuzz Coffee?. Tea instruction Boil water Put tea leaves in boiling water Pour the mix in cup Add lemon. Coffee instruction Boil water Brew coffee in boiling water Pour the mix in cup Add sugar and milk. Coffee boilWater ()

alta
Download Presentation

Design Pattern 2 [FF 6 – 11]

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Design Pattern 2[FF 6 – 11]

  2. Remember Starbuzz Coffee? Tea instruction Boil water Put tea leaves in boiling water Pour the mix in cup Add lemon Coffee instruction Boil water Brew coffee in boiling water Pour the mix in cup Add sugar and milk Coffee boilWater() brewCoffee() pourMix() addSugarMilk() prepare() prepare() { boilWater() brewCoffee() pourMix() addSugarMilk() } Tea boilWater() brewTea() pourMix() addLemon() prepare() Can we improve this design?

  3. Factor out common things to a superclass prepare() { boilWater() brewCoffee() pourMix() addSugarMilk() } Coffee brewCoffee() addSugarMilk() prepare() prepare() { boilWater() brew () pourMix() addCondiments() } <<abstract>> HotBeverage boilWater() pourMix() prepare() // abs you can even finalize it prepare() { boilWater() brewTea() pourMix() addLemon() } Tea brewTea() addLemon() prepare() What else can be improved?

  4. The new design Coffee brew() addCondiments () <<abstract>> HotBeverage boilWater() pourMix() prepare() brew() addCondiments() “prepare” is no longer abstract! Tea brew() addCondiments() final prepare() { boilWater() brew () pourMix() addCondiments() } The method “prepare” is a so-called “template method”; “brew” and “addCondiments” are called “hooks”. So this “pattern” here is called “Template Method Pattern”. So how easy is it now to add Chocolate??

  5. Another example public class MyApplet extends Applet { public void init() { start to pre-load that film! load main image repaint() } public void stop() { if film is playing, stop it } public void start() { continue film } public void paint() {...} } These are hooks, used by a number of template methods in the superclass Applet.

  6. Generic remote control Device RemoteControl slots : Device[8] assignSlot(slotNr,device) turnOn(slotNr) turnOff(slotNr) undo() CeilingFan low() medium() high() off() getSpeed() OutdoorLight turnOn() turnOff() Stereo turnOn() putcd() setvolume(v) turnOff() • We need to design this generic RC for HomeAutomation Inc • Slots must be dynamically assignable • HA provides a bunch of “devices” • Unfortunately they have varying interface • Furthermore ... expect more devices will come in the future

  7. First solution RemoteControl slot : Device[8] assignSlot(slotNr,device) turnOn(slotNr) turnOff(slotNr) undo() • Behavior that dynamically depends on subtypes • Remember the Factory solution ? • We need to do something similar, but with dynamic behavior selection. • (this presentation deviates a bit from FF) turnOn(n) { d = slot[n] if d is an OutdoorLight then d.turnOn() else if d is a Stereo then { d.turnOn() d.putCD() d.setVolume(10) } else ... }

  8. Encapsulating ... Device LightOnCmd LightOffCmd RemoteControl onCmds : Command[8] offCmds : Command[8] assignSlot(slotNr,device) turnOn(slotNr) turnOff(slotNr) undo() Command execute() StrereoOnCmd StrereoOffCmd We encapsulate each “sub-type” of behavior in its own “Command” class. turnOn(n) { onCmds[n].execute() } turnOff(n) { offCmds[n].execute() } our “behavior factory method” : assignSlot(n,device) { onCmds[n] = device.createOnCmd() ; offCmds[n] = device.createOffCmd() ; }

  9. How client requests are now handled.. execute() { d = (OutdoorLight) device d.turnOn() ; } OutdoorLightOnCmd Device OutdoorLightOffCmd Command execute() StrereoOnCmd execute() { d = (Strereo) device d.turnOn() ; d.putCD() ; d.setVolume(10) } StrereoOffCmd OutdoorLightOnCmd OutdoorLight :RC :Command :Device turnOn(1) execute() turnOn() putCD() setVolume(10)

  10. Adding Fan-commands and Undo OutdoorLightOnCmd undo() { (OutdoorLight) device . turnOff() } Device OutdoorLightOffCmd FanCmd oldState Command execute() undo() execute() { d = (CeilingFan) device speed = d.getSpeed() if speed is 0 then d.low() else if speed is low then d.medium() … oldstate = speed } FanOnCmd FunOffCmd undo() { d = (CeilingFan) device if oldstate is off then d.turnOff() else if oldstate is low then d.low() ... }

  11. Now implementing “undo” on the RC ... turnOn(n) { c = onCmds[n] c.execute() lastCmd = c } RemoteControl onCmds : Command[8] offCmds : Command[8] assignSlot(slotNr,device) turnOn(slotNr) turnOff(slotNr) undo() - lastCmd : Command undo() { lastCmd.undo() lastCmd = new DoNothingDevice() }

  12. The “Command Pattern” Device Receiver operation1 operation2 Device Client <<interface>> Command execute() undo() <<create>> Concrete Cmd 1 RemoteControl Invoker Concrete Cmd 2 Encapsulate behavior as an object; like normal object, these objects can be e.g. grouped together, send, and manipulated, before we execute the behavior they represent. (different formulation than FF)

  13. Example of things you can do execute() { for (i=0; i<#cmds; i++) cmds[i].execute() } • You can stream commands, and have multiple threads consuming them • logging and reload  useful when the “invoker” crashes MacroCmd cmds : Command[] execute() undo() undo() { for (i=#cmds; 0<i; i--) cmds[i-1].undo() }

  14. The merging of 3 restaurants... DinnerMenu menu : MenuItem[] addItem(item) • They can agree on a common class for “menu item” • But refuse to change how the menus are internally represented  too much of their respective software already depend on it * MenuItem name desc isVeggie price MenuWebInterface printFullMenu() printVeggies() printLowBudget() PancakeHouseMenu menu : ArrayList addItem(item) * * CafeMenu menu : HashTable addItem(item) (“Waitress” in your book)

  15. Let’s now implement the MenuWebInterface … printFullMenu() { dinnerItems = dinnerMenu.items // array for (i=0; i<dinnerItems.size; i++) print (dinnerItem[i]) lunchItems = pancakeHouseMenu.items // listarray for (i=0; i<lunchItems.size(); i++) print ((MenuItem) lunchItems.get(i)) drinks = cafeMenu.items.getValues() // values of hash-table for (i=0; i<drinks.size(); i++) print ((MenuItem) drinks.get(i)) } Similar iterations in implementing the other print operations

  16. Encapsulating iterations DinnerMenu - menu : MenuItem[] addItem(item) Menu getName() createIterator() // you can hide it now <<create>> PancakeHouseMenu - menu : ArrayList addItem(item) (abs. class) <<interface>> Iterator hasNext() : bool next() : Object createIterator() { return new DinnerMenuIterator(menu) } DinnerMenuIterator PancakeMenuIterator class DinnerMenuIterator extends Iterator { MenuItem[] items intctr DinnerMenuIterator(items) { this.items = items ; ctr = -1 } hasNext() { return ctr < #items - 1 } next() { ctr++ ; return items[ctr] } } Iterator pattern (deviate a bit from FF)

  17. Reworking MenuWebInterface MenuWI printMenu() { for (Menu m : menus) { print m.getName() iter = m.getIterator() while (iter.hasNext()) { i = (MenuItem) iter.next() ; print(i) ; } } menus * <<interface>> Menu getName() createIterator() : MenuWI : DinnerMenu printMenu() createIterator() <<create>> : DinnerMenuIterator hasNext() next() etc …

  18. Iterator in Java • Using the pattern amounts to subclass it • Standard collection classes already come with their iterator-classes. • “remove” is optional. It removes the element the element you just get from next() ; can only be called once per next() call. • if you change the underlying ‘collection’ during an iteration by any other way than using remove(), the behavior of the Iterator is undefined. <<interface>> Iterator hasNext() : bool next() : Object remove() : void Java.Util.Interface i = V.iterator() ; while (i.hasNext()) { x = i.next() ; if (x == Bob) V.add(Patrick) ; print(x) ; } // will Patrick be printed ?

  19. While we are talking about Java iterators... Before Java 1.2 : <<interface>> Iterator hasNext() : bool next() : Object remove() : void <<interface>> Enumeration hasMoreElements() : bool nextElement() : Object 1.6 Client <<use>> 1 Concrete EnumerationA EnumerationAdapter constructor(enumeration) Suppose we have Java 1.1 legacy code; new features are to be added in 1.6. In the new part we prefer to use Iterator; but how to upgrade your old Enumeration classes to Iterators?

  20. Implementation <<interface>> Iterator hasNext() : bool next() : Object remove() : void <<interface>> Enumeration hasMoreElements() : bool nextElement() : Object 1.6 Client <<use>> 1 enum Concrete EnumerationA EnumerationAdapter enum constructor(enumeration) A getEnum() : ConcreteEnumerationA EnumerationAdapter(e) { enum = e } For client now to iterate over A : e = instanceA.getEnum() i = new EnumerationAdapter(e) hasNext() { return enum.hasMoreElements() } next() { return enum.nextElement() } remove() ...??

  21. That was the “adapter pattern” Iterator Target operations Client Enumeration <<use>> 1 Adaptee other operations Adapter • We’ll discuss two more variants: Proxy and Facade. • Not too complicated

  22. Adding a reporting function to parking machines ParkingMachine location() count() hours() machines Report + printReport () * printReport() { for (Machine m : machines) { print m.location() print m.count() print m.hours() } } Does not work if the parking machines objects exist on physically different computers. We need a networking solution, but want a nice way to fit that into our design.

  23. Proxy pattern Create a “stand-in” for another object; useful when the real object is remote, or expensive to create, or require securing. subject <<interface>> ParkingMachineInterface location() count() hours() * ParkingMachineProxy ParkingMachine location() count() hours() ParkingMachine Report + printReport () real subject remote side client side The proxy pass requests to the actual subject; implying it implements this passing mechanism, e.g. over Internet

  24. Home cinema

  25. Ok.. let’s watch a film now • Complicated... • How about when we are done watching film. • How about listening to music? etc • What if we upgrade the system?

  26. Facade pattern To provide a simplified interface. HomeCinemaFacede watchFilm() listenCD() <<use>> Client • So... can you say when it is more appropriate to use one of these over the other ? • Adapter • Proxy • Facade • Decorator

  27. insert coin Candy machine… turn crank has coin turn crank / [counter is updated] [1eur] get candy insert coin [else] / [coin is ejected] sold no coin • CandyMachine • state • + insertCoin(c) • + turnCrank() • + getCandy() [not empty] get candy [empty] empty

  28. Mapping this to implementation class CandyMachine { static final NOCOIN = 0 static final HASCOIN = 1 static final SOLD = 2 static final EMPTY = 3 private state private n CandyMachine() { this.n = 200 state = NOCOIN } } insertcoin(c) { if (state == NOCOIN) { if (c is 1 euro) state = HASCOIN else eject c } else eject c } getcandy() { if (state == SOLD) { n-- if (n==0) state = EMPTY else state = NOCOIN } else skip }

  29. The company wants to add usr messaging insertcoin(c) { if (state == NOCOIN) { if (c is 1 euro) state = HASCOIN else { print “Not 1 euro” eject c } } else if (state == HASCOIN || state == SOLD) { print “You have inserted a coin” eject c } else if (state == EMPTY) { print “the machine is empty” eject c } } getcandy() { if (state == SOLD) { if (n==0) state = EMPTY else state = NOCOIN } else if (state == NOCOIN) print “You have not inserted a coin” else if (state == HASCOIN) print “You =have not turned the crank” … }

  30. The company wants to add prize ball! turn crank [n>1] / [counter is updated] has coin [1eur] turn crank / [counter is updated] insert coin [else] / [coin is ejected] sold bonus no coin get candy turn crank / [counter is updated] [not empty] get candy Implementation blows! [empty] empty

  31. Encapsulating state NoCoinSt candyMachine - n : int + insertCoin(c) + turnCrank() + getcandy() CMstate insertCoin(c) turnCrank() getcandy() HasCoinSt owner (current) state SoldSt 1 0..1 EmptySt (abstract class) candyMachine() { n = 200 state = new NoCoin(this) } NoCoinSt(machine) { owner = machine } insertCoin(c) { state.insertCoin(c) } turnCrank() { state.turnCrank() } getcandy() { state.getcandy() } Simple candy Machine !

  32. Implementing the states getcandy() { print “you have not inserted a coin” } NoCoinSt CMstate insertCoin(c) turnCrank() getcandy() owner candy Machine HasCoinSt 0..1 1 getcandy() { print “you must first turn the crank” } } … insertcoin(c) { if (state == NOCOIN) { if (c is 1 euro) state = HASCOIN else { print “Not 1 euro” eject c } } else if (state == HASCOIN || state == SOLD) { print “You have inserted a coin” eject c } else if (state == EMPTY) { print “the machine is empty” eject c } } insertCoin(c) { if (c is 1 euro) owner.state = new HasCoinSt(owner) else { print “not 1 euro coin” ; eject c } } insertCoin(c) { print “you have inserted a coin” ; eject c } } getcandy() { if (state == SOLD) { if (n==0) state = EMPTY else state = NOCOIN } else if (state == NOCOIN) print “You have not inserted a coin” else if (state == HASCOIN) print “You =have not turned the crank” … }

  33. What is the point of this? NoCoinSt candyMachine - n : int + insertCoin(c) + turnCrank() + getcandy() CMstate insertCoin(c) turnCrank() getcandy() HasCoinSt owner (current) state SoldSt 1 0..1 EmptySt BonusSt • What need to be done ? • Obviously implement BonusSt this implements all arrows leaving Bonus • We still have to code incoming arrows  turnCrank of HasCoinSt • The changes in the existing code is minimized!

  34. State pattern Extensible implementation of state-machine. (dev. from FF) Concrete State 1 State operation1(..) operation2(..) StMachine operation1(..) operation2(..) (current) state owner Concrete State 2 0..1 1 … FF: <<interface>> State handle(..) Concrete State 1 Context request(..) Concrete State 2 … request(..) { state.handle(..) }

  35. Back to our restaurants… DinnerMenu Menu PancakeHouseMenu * MenuItem CafeMenu (deviate a bit from FF)

  36. What if we plan to add submenus in the future? all menus : PancakeMenu : CafeMenu : DinnerMenu MenuItems : Dessert Menu • We need a tree structure. • And a convenient and extensible way to implement computations that traverse the tree, like: • Counting the menu items • Get the cheapest menu item • Checking if a menu item is present

  37. Composite pattern operation  e.g. get all leaves Component operation(..) add(component) remove(component) getChild(k) :Composite components * :Composite :Leaf :Leaf :Leaf :Leaf :Composite Composite operation(..) add(component) remove(component) getChild(k) // or getChildren() .. or ret. iterator Leaf operation(..) :Leaf :Leaf (FF’s formulation of the pattern)

  38. Discussion Component operation(..) add(component) remove(component) getChild(k) components * children () : List<Component> (alternatively, return an iterator) Leaf operation(..) Composite operation(..) add(component) remove(component) getChild(k) • Forcing some operations on Leaf • Circularity • getChild does not seem very useful

  39. Implementation class Menu { components = new LinkedList<...>() add(c) { components.add(c) } remove(c) { components.remove(c) } } <<interface>> MenuComponent operation(..) components * MenuItem name desc price Menu add(component) remove(component)

  40. Operation : counting menu items <<interface>> MenuComponent countMenuItems() class Menu { components … countMenuItems() { n = 0 for (MenuComponent m : components) n += m.countMenuItems()) return n } } components * MenuItem name desc price Menu add(component) remove(component) class MenuItem { countMenuItems() { return 1 } }

  41. Operation : getting the cheapest menu item <<interface>> MenuComponent countMenuItems() getCheapest() class Menu { components … getCheapest() { p = MAXINT c = null for (MenuComponent m : components) { d = m.getCheapest() if (d != null && d.price < p) { c = d ; p = d.price } } return c } } components * MenuItem name desc price Menu add(component) remove(component) class MenuItem { getCheapest() { return this } }

More Related