Software Architecture Bertrand Meyer & Till Bay ETH Zurich, February-May 2008 Lecture 7: Visitor, Strategy, State, Chain of Responsibility, Command Program overview Date Topic Who? last week Introduction; A Basic Architecture Example Till 26. Feb. Modularity and reusability; Abstract Data Types Till 4. Mar. Project description and Delta Debugging Jason, Andy 11. Mar. Patterns 1: observer + event library, componentization Till 18. Mar. Design by Contract Prof. Meyer 25. Mar. No course :-) Today Till Patterns 2: visitor, strategy, state, chain of responsibility 8. Apr. Patterns 3: factory, builder, singleton Till Michela 15. Apr. Patterns 4: bridge, composite, decorator, facade 22. Apr. Patterns 5: Wrap up Till 29. Apr. Language constructs for mod. and info. hiding Till Program overview Date Topic Who? 6. May Exception handling Martin 13. May Concurrent programming Jason 20. May Project presentation Everybody 27. May Exam Everybody Exercises overview Date Topic 28. Feb. Abstract Data Types 3. Apr. Design by Contract 8. May Exception Handling Rest Project
visitor • Process the elements of an (unbounded) data structure. • Apply computations depending on the type of data node. • Store the code outside of the data structure. • Most of the time used together with the composite pattern. comerge inc. visitor example INTEGER_ ADDITION_ MULTIPLICATION_ EXPRESSION EXPRESSION EXPRESSION Compute the left Compute the left EXPRESSION_ Return the integer and right subtree, and right subtree, COMPUTATION_ value. then add the then multiply the VISITOR Double dispatch values. values. Generate code for Generate code for the left and right the left and right CODE_ Generate code subtree, then code subtree, then code GENERATOR_ that stores the that pops two that pops two VISITOR value on the stack. elements off the elements off the stack and adds stack and multiplies them. them. comerge inc. double dispatch • “Double dispatch” is code selection based on the dynamic type of two objects. • Available in some languages like Dylan, MultiJava, LISP, Python (via language extensions) • Not available in Eiffel • Can be emulated by dispatching twice : dynamic binding on the visitor and the subject • Caveat: Preparation needed comerge inc.
double dispatch process (Current) EXPRESSION_ ADDITION_ COMPUTATION_ EXPRESSION VISITOR process_addition (Current) comerge inc. abstract visitor deferred class EXPRESSION_VISTOR feature -- Processing process_integer (e: INTEGER_EXPRESSION) is -- Process integer constants. deferred end process_addition (e: ADDITION_EXPRESSION) is -- Process additions. deferred Some end contracts process_multiplication (e: skipped for MULTIPLICATION_EXPRESSION) is brevity! -- Process multiplications. deferred end end comerge inc. process features deferred class EXPRESSION feature -- Processing process (v: EXPRESSION_VISITOR) is -- Process this node by visitor `v ʼ . require not_void: v /= Void deferred end end comerge inc.
process features class INTEGER_EXPRESSION inherit EXPRESSION feature -- Access value: INTEGER -- Representation of expression feature -- Processing process (v: EXPRESSION_VISITOR) is -- Process this node by visitor `v ʼ . do v.process_integer (Current) end end comerge inc. process features class ADDITION_EXPRESSION inherit EXPRESSION ... feature -- Processing process (v: EXPRESSION_VISITOR) is -- Process this node by visitor `v ʼ . do v.process_addition (Current) end ... end comerge inc. concrete visitor class EXPRESSION_COMPUTATION_VISTOR feature -- Access last_result: INTEGER feature -- Processing process_integer (e: INTEGER_EXPRESSION) is -- Process integer constants. do last_result := e.value end comerge inc.
concrete visitor process_addition (e: ADDITION_EXPRESSION) is -- Process additions. local right_value, left_value: INTEGER do e.left.process (Current) left_value := last_result e.right.process (Current) right_value := last_result last_result := right_value + left_value end -- (same for process_multiplication) end comerge inc. iterating visitor class EXPRESSION_ITERATOR inherit EXPRESSION_VISITOR feature -- Processing process_integer (e: INTEGER_EXPRESSION) is -- Process integer constants. do end process_addition (e: ADDITION_EXPRESSION) is -- Process additions. do e.left.process (Current) e.right.process (Current) end ... end comerge inc. visitor summary • Elegant solution to traverse unbounded data structures • Emulating double dispatch • Needs preparation • Introducing new node types requires the adaptation of the abstract visitor and its children • Result values are stored in attributes comerge inc.
Strategy Purpose “Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it”. [Gamma et al., p 315] Example application selecting a sorting algorithm on-the-fly comerge inc. Life without strategy: a sorting example feature -- Sorting sort (a_list: LIST [INTEGER]; a_strategy: INTEGER) is -- Sort a_list using algorithm indicated by a_strategy. require a_list_not_void: a_list /= Void a_strategy_valid: is_strategy_valid (a_strategy) do inspect a_strategy when binary then … when quick then … What if a new algorithm is needed ? when bubble then … else … end ensure a_list_sorted: … end comerge inc. Strategy pattern: overall architecture + strategy * do_something* CONTEXT STRATEGY do_something + + + STRATEGY_A STRATEGY_B STRATEGY_C do_something+ do_something+ do_something+ comerge inc.
Class STRATEGY deferred class STRATEGY feature -- Basic operation do_something -- Do something (perform some algorithm). deferred end end comerge inc. Class CONTEXT class CONTEXT create make feature {NONE} -- Initialization make (a_strategy: like strategy) is -- Set strategy to a_strategy. require a_strategy_not_void: a_strategy /= Void do strategy := a_strategy ensure strategy_set: strategy = a_strategy end comerge inc. Class CONTEXT feature – Basic operations do_something -- Do something (call algorithm cooresponding to strategy ). do strategy.do_something end set_strategy (a_strategy: like strategy) -- Set strategy to a_strategy. require a_strategy /= Void do strategy := a_strategy ensure strategy = a_strategy end comerge inc.
Class CONTEXT feature {NONE} – Implementation strategy: STRATEGY -- Strategy to be used invariant strategy_not_void: strategy /= Void end comerge inc. Using strategy pattern sorter_context: SORTER_CONTEXT Now, what if a new algorithm is needed ? bubble_strategy: BUBBLE_STRATEGY hash_strategy: HASH_STRATEGY quick_strategy: QUICK_STRATEGY create sorter_context.make (bubble_strategy) sorter_context.sort (a_list) sorter_context.set_strategy (hash_strategy) sorter_context.sort (a_list) sorter_context.set_strategy (quick_strategy) sorter_context.sort (a_list) comerge inc. Strategy - Consequences • Families of related algorithms • An alternative to subclassing • Eliminate conditional statements • A choice of implementations • Clients must be aware of different Strategies • Communication overhead between Strategy and Context • Increased number of objects comerge inc.
Strategy - Participants Strategy declares an interface common to all supported algorithms. Concrete strategy implements the algorithm using the Strategy interface. Context is configured with a concrete strategy object. maintains a reference to a strategy object. comerge inc. Chain of responsibility Purpose “Avoid[s] coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. [It] chain[s] the receiving objects and pass[es] the request along the chain until an object handles it.” [Gamma et al., p 223] Example application A GUI event is passed from level to level (such as from button to dialog and then to application) comerge inc. Chain of responsibility * HANDLER [ G ] APPLICATION next handle can_handle* do_handle* handled set_next + + INTERMEDIATE_ FINAL_HANDLER [ G ] HANDLER [ G ] can_handle can_handle+ + do_handle+ do_handle + 35 comerge inc.
Class HANDLER [ G ] deferred class HANDLER [G] create default_create, make feature {NONE } -- Initialization make (n : like next) is -- Set next to n. do next := n ensure next_set: next = n end feature -- Access next : HANDLER [G] -- Successor in the chain of responsibility feature -- Status report can_handle (r : G): BOOLEAN is deferred end -- Can this handler handle r? handled: BOOLEAN -- Has request been handled? comerge inc. Class HANDLER [ G ] feature -- Basic operations handle (r : G) is -- Handle r if can_handle otherwise forward it to next. -- If no next, set handled to False . do if can_handle (r ) then do_handle (r ) ; handled := True else if next /= Void then next.handle (r ) ; handled := next.handled else handled := False end end ensure can_handle (r ) implies handled ( not can_handle (r ) and next /= Void ) implies handled = next.handled ( not can_handle (r ) and next = Void ) implies not handled end comerge inc. Class HANDLER [ G ] feature -- Element change set_next (n : like next) is -- Set next to n. do next := n ensure next_set: next = n end feature {NONE} – Implementation do_handle (r : G) is -- Handle r. require can_handle: can_handle (r) deferred end end comerge inc.
Recommend
More recommend