1 / 82

Software Architecture

Software Architecture. Bertrand Meyer. Lecture 6: More patterns: Visitor, Strategy, Chain, State, Command. Command. 2. Purpose Way to implement an undo-redo mechanism, e.g. in text editors. [OOSC, p 285-290]

luana
Download Presentation

Software Architecture

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. Software Architecture Bertrand Meyer Lecture 6: More patterns: Visitor, Strategy, Chain, State, Command

  2. Command 2

  3. Purpose Way to implement an undo-redo mechanism, e.g. in text editors. [OOSC, p 285-290] ”Way to encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.” [Gamma et al., p 233] Application example EiffelStudio Command pattern - Intent 3

  4. The problem Enabling users of an interactive system to cancel the effect of the last command. Often implemented as “Control-Z”. Should support multi-level undo-redo, with no limitation other than a possible maximum set by the user A good review of O-O techniques

  5. Working example: text editor Notion of “current line”. Assume commands such as: • Insert line after current position • Insert line before current position • Delete current line • Replace current line • Swap current line with next if any • ... This is a line-oriented view for simplicity, but the discussion applies to more sophisticated views

  6. Underlying class (from “business model”) class EDIT_CONTROLLER feature text: LINKED_LIST [STRING] remove -- Remove line at current position. require notoff do text.remove end put_right(line: STRING) -- Insert line after current position. require notafter do text.put_right(line) end ... end

  7. Key step in devising a software architecture Here: The notion of “command” Finding the right abstractions (Interesting object types)

  8. Keeping the history of the session The history list: Insert Insert Remove Insert Swap Oldest Most recent history: LINKED_LIST [COMMAND]

  9. What’s a “command” object? An instance of COMMAND includes information about one execution of a command by the user, sufficient to: • Execute the command • Cancel the command if requested later • For example, in a delete command object, we need: • The position of the line being deleted • The content of that line

  10. General notion of command feature deferred class COMMAND done: BOOLEAN-- Has this command been executed? execute-- Carry out one execution of this command. deferred : done end ensurealready:done undo -- Cancel an earlier execution of this command. requirealready:done deferredend end

  11. Command class hierarchy * execute* undo* * COMMAND deferred + effective + + … DELETION INSERTION execute+undo+index ... execute+undo+lineindex ...

  12. A command class (sketch, no contracts) class DELETION inherit COMMAND feature controller: EDIT_CONTROLLER -- Access to business model line: STRING -- Line being deleted index: INTEGER -- Position of line being deleted execute -- Remove current line and remember it. do line:=controller.item;index:=controller.index controller.remove;done:=True end undo -- Re-insert previously removed line. do controller.go_ith(index) controller.put_left(line) end end

  13. Executing a user command decode_user_request if “Request normal command” then “Create command object c corresponding to user request” history.extend(c) c.execute elseif “Request UNDO” then if not history.beforethen -- Ignore excessive requests history.item.undo history.back end elseif“Request REDO” then if not history.is_lastthen -- Ignore excessive requests history.forth history.item.execute end end Remove Insert Insert Insert item

  14. Command pattern: overall architecture * COMMAND commands history APPLICATION HISTORY execute* undo* redo* execute can_undo, can_redo undo, redo undo_all, redo_all extend + COMMAND_2 + COMMAND_1 execute+ undo+ redo+ execute+ undo+ redo+ 14

  15. The undo-redo pattern Has been extensively used Fairly easy to implement Details must be handled carefully (e.g. some commands may not be undoable) Elegant use of O-O techniques Disadvantage: explosion of small classes In Java, can use “inner” classes.

  16. Using agents For each user command, have two routines: • The routine to do it • The routine to undo it!

  17. The history list using agents The history list simply becomes a list of agents pairs: history: LINKED_LIST [TUPLE [PROCEDURE [ANY, TUPLE], PROCEDURE [ANY, TUPLE]] Basic scheme remains the same, but no need for command objects any more; the history list simply contains agents. Insert Insert Remove Insert Swap De-insert De-insert Re-insert De-insert Swap

  18. Executing a user command (before) decode_user_request if “Request is normal command” then “Create command object c corresponding to user request” history.extend(c) c.execute elseif “Request is UNDO” then if not history.beforethen --Ignoreexcessiverequests history.item.undo history.back end elseif“Request is REDO” then if not history.is_lastthen --Ignoreexcessiverequests history.forth history.item.execute end end Remove Insert Insert Insert item

  19. Executing a user command (now) “Decode user_request giving two agentsdo_itand undo_it” if “Request is normal command” then history.extend([do_it, undo_it]) do_it.call([]) elseif “Request is UNDO” then if not history.beforethen history.item.item(2) .call ([]) history.back end elseif“Request is REDO” then if not history.is_lastthen history.forth history.item.item(1) .call ([])end end Insert Insert Remove Swap De-insert De-insert Re-insert Swap

  20. Command decouples the object that invokes the operation from the one that knows how to perform it. Commands are first-class objects. They can be manipulated and extended like any other object. You can assemble commands into a composite command. It's easy to add new Commands, because you don't have to change existing classes. Command - Consequences 20

  21. Command declares an interface for executing an operation. Concrete command defines a binding between a Receiver object and an action. implements Execute by invoking the corresponding operation(s) on Receiver. Client creates a ConcreteCommand object and sets its receiver. Invoker asks the command to carry out the request. Receiver knows how to perform the operations associated with carrying out a request. Any class may serve as a Receiver. Command - Participants 21

  22. Some design patterns Creational • Abstract Factory • Builder • Factory Method • Prototype • Singleton Structural • Adapter • Bridge • Composite • Decorator • Façade • Flyweight • Proxy Behavioral • Chain of Responsibility • Command (undo/redo) • Interpreter • Iterator • Mediator • Memento • Observer • State • Strategy • Template Method • Visitor

  23. Visitor 23

  24. “Represents an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.” [Gamma et al., p 331] Static class hierarchy Need to perform traversal operations on corresponding data structures Avoid changing the original class structure Visitor - Intent 24

  25. Set of classes to deal with an Eiffel or Java program (in EiffelStudio, Eclipse ...) Or: Set of classes to deal with XML documents (XML_NODE, XML_DOCUMENT, XML_ELEMENT, XML_ATTRIBUTE, XML_CONTENT…) One parser (or several: keep comments or not…) Many formatters: Pretty-print Compress Convert to different encoding Generate documentation Refactor … Visitor application example 25

  26. Inheritance hierarchy center * display* * rotate* FIGURE * * OPEN_ FIGURE perimeter* CLOSED_ FIGURE perimeter + perimeter + + SEGMENT POLYLINE + POLYGON ELLIPSE ... ... side1 RECTANGLE side2 TRIANGLE diagonal perimeter ++ * deferred + effective ++ redefined perimeter ++ CIRCLE SQUARE perimeter ++

  27. Polymorphic data structures figs: LIST [FIGURE] (ELLIPSE) (POLYGON) (CIRCLE) (POLYGON) (CIRCLE) from figsstart until figsafter loop figsitemdisplay figsforth end

  28. The dirty secret of O-O architecture Is it easy to add types (e.g.LOZENGE) to existing operations center * display* * rotate* FIGURE * * OPEN_ FIGURE perimeter * CLOSED_ FIGURE perimeter + perimeter + + SEGMENT POLYLINE + POLYGON ELLIPSE ... perimeter ++ ... side1 RECTANGLE side2 TRIANGLE diagonal perimeter ++ CIRCLE SQUARE perimeter ++ What about the reverse: adding an operation to existing types?

  29. Vehicle example * VEHICLE + BUS + TAXI We want to add external functionality, for example: • Maintenance • Schedule a vehicle for a particular day 29

  30. Vehicle operations Why is this approach bad ? schedule (v: VEHICLE, d: DAY) -- Set up schedule of v for d. require exists:v /=Void do if attached {BUS}v as b then bschedule (d) end if attached {TAXI}v as t then tput_on_call(d) end ... Other cases ... end end maintain (v: VEHICLE) -- Perform maintenance on v. require exists:v /=Void do if attached {BUS}v as b then bsend_to_depot end if attached {TAXI}v as t then tsend_to_garage (Next_monday) end ... Other cases ... end end 30

  31. The original view CLIENT visit T_TARGET Client (calls)

  32. Target classes Example: BUS, TAXI Client classes Application classes that need to perform operations on target objects Visitor classes Written only to smooth out the collaboration between the other two Visitor participants 32

  33. Visitor General notion of visitor Concrete visitor Specific visit operation, applicable to all target elements Target General notion of visitable element Concrete target Specific visitable element Visitor participants 33

  34. The original view CLIENT visit T_TARGET Client (calls)

  35. The visitor ballet CLIENT taccept (v) v T_TARGET V_VISITOR vT_visit(Current) Client(knows about) Client (calls)

  36. Visitor class hierarchies accept* vT_visit(Current) * VISITOR * VEHICLE visit_bus* visit_tram* + BUS + TAXI + MAINTENANCE_VISITOR + SCHEDULE_VISITOR accept+ accept+ visit_taxi+ visit_taxi+ visit_bus+ visit_bus+ Target classes Visitor classes 36

  37. class MAINTENANCE_VISITOR inherit VISITOR feature-- Basic operations visit_taxi(t: TAXI) -- Perform maintenance operations ont. do tsend_to_garage (Next_monday) end visit_bus(b:BUS) -- Perform maintenance operations on b. do bsend_to_depot end end The maintenance visitor 37

  38. class MAINTENANCE_VISITOR inherit VISITOR feature-- Basic operations visit_taxi(t: TAXI) -- Perform scheduling operations ont. do ... end visit_bus(b:BUS) -- Perform scheduling operations on b. do ... end end The scheduling visitor 38

  39. class BUS inherit VEHICLE feature accept (v: VISITOR) -- Apply bus visit to v. do v.visit_bus (Current)‏ end end Changes to the target classes deferred class VEHICLE feature ... Normal VEHICLE features ... accept (v: VISITOR) -- Apply vehicle visit to v. deferred end end class TAXI inherit VEHICLE feature accept (v: VISITOR) -- Apply taxi visit to v. do v.visit_taxi (Current)‏ end end 39

  40. The full picture vT_visit(Current) * VISITOR accept* * VEHICLE visit_bus* visit_tram* Target classes Visitor classes vT_visit(Current) + MAINT_VISITOR + SCHEDULE_VISITOR + TAXI + V_VISITOR + T + BUS accept+ accept+ accept+ visit_taxi+ visit_taxi+ visit_taxi+ visit_bus+ visit_bus+ visit_bus+ v taccept (v) Example client calls: bus21.accept (maint_visitor) fleet.item.accept (maint_visitor) CLIENT 40

  41. Makes adding new operations easy Gathers related operations, separates unrelated ones Avoids assignment attempts Better type checking Adding new concrete element is hard Visitor - Consequences Does the visitor pattern observe the Open-Closed Principle ? 41

  42. Dynamic binding: Easy to add types Hard to add operations Visitor: Easy to add operations Hard to add types Visitor vs dynamic binding 42

  43. The visitor pattern vT_visit(Current) * VISITOR accept* * VEHICLE visit_bus* visit_tram* Target classes Visitor classes vT_visit(Current) + MAINT_VISITOR + SCHEDULE_VISITOR + TAXI + V_VISITOR + T + BUS accept+ accept+ accept+ visit_taxi+ visit_taxi+ visit_taxi+ visit_bus+ visit_bus+ visit_bus+ v taccept (v) Example client calls: bus21.accept (maint_visitor) fleet.item.accept (maint_visitor) CLIENT 43

  44. One generic class VISITOR [G] e.g. maintenance_visitor:VISITOR [VEHICLE] Actions represented as agents actions:LIST [PROCEDURE [ANY, TUPLE [G]]] No need for accept features visitdetermines the action applicable to the given element For efficiency Topological sort of actions (by conformance) Cache (to avoid useless linear traversals) The Visitor Library 44

  45. class VISITOR[G] create make feature{NONE} -- Initialization make --Initializeactions. feature--Visitor visit(an_element: G) --Selectactionapplicabletoan_element. require an_element_not_void: an_element/= Void feature--Access actions: LIST[PROCEDURE[ANY, TUPLE[G]]] --Actions tobeperformeddependingontheelement Visitor Library interface (1/2) 45

  46. feature-- Element change extend(action: PROCEDURE[ANY, TUPLE[G]]) --Extendactionswithaction. require action_not_void: action/= Void ensure one_more: actions.count= oldactions.count+ 1 inserted: actions.last= action append(some_actions: ARRAY[PROCEDURE[ANY, TUPLE[G]]]) --Appendactionsinsome_actions --totheendoftheactionslist. require some_actions_not_void: some_actions/= Void no_void_action: notsome_actions.has(Void) invariant actions_not_void: actions/= Void no_void_action: notactions.has(Void) end Visitor Library interface (2/2) 46

  47. maintenance_visitor: VISITOR[BORROWABLE] createmaintenance_visitor.make maintenance_visitor.append([ agentmaintain_taxi, agentmaintain_trolley ]) maintain_taxi(a_taxi: taxi)... maintain_trolley(a_trolley: TROLLEY)... Using the Visitor Library , agent maintain_tram maintain_tram(a_tram: BUS)... 47

  48. Topological sorting of agents (1/2) * BORROWABLE * BUS * VEHICLE + TROLLEY + TRAM 48

  49. schedule_visitor.extend(agentschedule_taxi) Topological sorting of agents (2/2) schedule_visitor.extend(agentschedule_bus) schedule_visitor.extend(agentschedule_vehicle) schedule_visitor.extend(agentschedule_tram) schedule_visitor.extend(agentschedule_trolley) For agent schedule_a(a: A) and schedule_b(b: B), if A conforms to B, then position of schedule_ais before position of schedule_bin the agent list schedule_taxi schedule_ bus schedule_ vehicle schedule_ vedio_tram schedule_ tram 1 5 2 4 3 schedule_visitor.visit(a_bus) 49

  50. Strategy 50

More Related