I. GRASP: More Patterns for Assigning Responsibilities (Larman, Chapter 22) A. new patterns, context ------------------------------------------ PREVIOUS GRASP PATTERNS FOR RESPONSIBILITY ASSIGNMENT Earlier patterns: - Information Expert - Creator - High-Cohesion - Low Coupling - Controller New Patterns: - Polymorphism - Indirection - Pure Fabrication - Protected Variations ------------------------------------------ B. Polymorphism (22.1) ------------------------------------------ POLYMORPHISM Problem: How to handle different types of objects with similar, but not identical behavior? How to create pluggable components? Solution: when behaviors or executions vary by type, assign responsibility for the variations to these types, using dynamic dispatch (virtual functions). Record the common parts in a supertype. ------------------------------------------ what's the problem with if-then-else's to handle different types? ------------------------------------------ EXAMPLE |----------------------| | <> | | Collection | |----------------------| |----------------------| | add(Object): boolean | | addAll(Collection): | | boolean | | clear() | | contains(Object): | | boolean | | ... | |----------------------| /_\ | |- - - - - - - - - - -| ... | | |------------------| |------------------| | AbstractList | | AbstractSet | |------------------| |------------------| | | | | |------------------| |------------------| | add(int, Object):| | equals(Object): | | boolean | | boolean | | add(Object): | | hashCode(): int | | boolean | | removeAll( | | clear() | | Collection): | | ... | | boolean | |------------------| |------------------| ------------------------------------------ What benefits from having the Collection interface? ------------------------------------------ Example: support different tax calculators |------------------| | <> | | TaxCalcAdapter | |------------------| |------------------| | getTaxes | | (Reservation): | | List of TaxLine | |------------------| /_\ | |- - - - - - - - - - -| ... | | |------------------| |------------------| | SuperTaxCalc | | EZTaxesCalculator| | Adapter | | Adapter | |------------------| |------------------| | | | | |------------------| |------------------| | getTaxes | | getTaxes | | (Reservation): | | (Reservation): | | List of TaxLine | | List of TaxLine | |------------------| |------------------| ------------------------------------------ Where did the if-then-else go? Where could you use this in your projects? C. pure fabrication (22.2) ------------------------------------------ PURE FABRICATION Problem: how to assign responsibility when using other patterns would violate high cohesion and low coupling, or other goals? Solution: use a made-up class, not in the problem domain ------------------------------------------ so how can you tell from the domain and design models if a class is a pure fabrication? ------------------------------------------ Example (airline reservations): Need to save reservations persistently. Why not let Reservation do that? - lots of database specific knowledge it doesn't have ==> low cohesion - coupling to DBMS classes ==> hurts reuse - different kind of responsibility So ------------------------------------------ how does this solve the problems with having Reservation save themselves persistently? Where could you use this in your projects? D. indirection (22.3) ------------------------------------------ INDIRECTION (22.3) Problem: how to de-couple objects so that they can change independently? How to avoid direct coupling? Solution: assign responsibility to an intermediary to avoid coupling other components and services. The intermediary creates an indirection between the other components. Examples: PersistentStorage is an intermediary between Reservation and the DBMS. TaxCalculatorAdapter is an intermediary between system and external tax calculators. ------------------------------------------ What are the benefits? Where could you use this in your projects? E. Protected Variations (22.4) ------------------------------------------ PROTECTED VARIATIONS Problem: How to isolate changes or variations? Solution: Assign responsibility to a stable interface that hides predicted instability Record the stable responsibilities in the interface. Examples: Tax calculation variations Marketing gimmicks change prices ------------------------------------------ How is this done in automobile design? In medicine? ------------------------------------------ Discussion: - Very old principle of design - Movivates lots of mechanisms: - abstract data types, encapsulation - templates (generic polymorphism) - polymorphism (subtype polymorphism) LSP, supertype abstraction - data-driven designs - service lookup - interpreters, virtual machines - reflective/meta-level designs - uniform access to arrays, methods, fields - structure-hiding designs Law of Demeter: should only send to - this (or self) - a parameter - an attribute of this, - an element of a collection contained in "this" - an object freshly created ------------------------------------------ ------------------------------------------ Contraindications: Def: a *variation point* is a difference that must be supported in the current system. Def: an *evolution point* is a difference that may arise in the future, but is not present in existing requirements. Be realistic and only protect evolution points that are likely and expensive to change. ------------------------------------------ What are the benefits? Where could you use this in your projects?