I. Designing Use Case Realizations with Gang of Four Design Patterns (Ch. 23) A. adapter (23.1) ------------------------------------------ ADAPTER (23.1, GoF p. 139ff) Problem: How to resolve incompatible interfaces? How to provide stable interface to similar components with different interfaces? Solution: An intermediate object that translates calls from one interface to the other. Example: ------------------------------------------ ------------------------------------------ |-----------------------------| | <> | | ITaxCalcAdapter | |-----------------------------| | getTaxes(Reservation): | | List of TaxLineItems | |-----------------------------| ------------------------------------------ How would you do this for accounting? Credit authorization? 1. Discussion 2. related patterns 3. analysis discoveries during design (23.2) B. Factory (23.3) 1. separation of concerns 2. factories ------------------------------------------ FACTORY (23.3, GoF p. 87ff and p. 107ff) Problem: who should create objects when: - the exact class will vary - creation logic is complex Solution: assign responsibility to a factory class Example: ------------------------------------------ ------------------------------------------ IMPLEMENTATION STRATEGIES FOR FACTORIES Compile it in: public class ServicesFactory { public ITaxCaclAdapter getTaxCalcAdapter() { return new TaxMasterAdpater(...); } } Get property (or read from a file): public ITaxCaclAdapter getTaxCalcAdapter() { String desired = System.getProperty( "taxcalc.class.name"); if (desired.equals("TaxMaster") { return new TaxMasterAdpater(...); } else if ( ...) { ... } else { throw new ClassNotFoundException(); } } ------------------------------------------ What's the problem with compiling it in? ------------------------------------------ REFLECTIVE IMPLEMENTATION (Larman 23.3) public ITaxCaclAdapter getTaxCalcAdapter() { String className = System.getProperty( "taxcalc.class.name"); return (ITaxCalcAdapter) Class.forName(className) .newInstance(); } ------------------------------------------ 3. discussion What's the advantage of using reflection? 4. related patterns 5. also known as C. Singleton (23.4) would be a good idea to pass the ServicesFactory class around the program? how many instances of the factory are needed? How many instances of the tax calculator adapter are needed? ------------------------------------------ SINGLETON (23.4, GoF pp. 127ff) Problem: How to ensure that a class has only one instance? How to provide global access to a single instance object? How to allow the instance to be extended by subtyping, without affecting clients? Solution: Use a static method (class operation) to return the instance. Hide the class's constructor. Example: ------------------------------------------ ------------------------------------------ JAVA CODING Hide the default constructor: public class ServicesFactory { private ServicesFactory() {} /* ... */ } Use a static field for the instance, and lazily initialize it in getInstance(): public class ServicesFactory { private ServicesFactory() {} private ServicesFactory instance = null; public static synchronized ServicesFactory getInstance() { if (instance == null) { instance = new ServicesFactory(); } //@ assert instance != null; return instance; } } ------------------------------------------ How could you ensure that there is only one instance of a tax calculator adapter as well? 1. UML shorthand interaction diagrams 2. implementation and design issues a. lazy vs. eager initialization b. static methods instead of the singleton pattern why not add a static method getAccountingAdapter to ServicesFactory? 3. related patterns D. summary of access to external services with varying interfaces (23.5) E. Strategy (23.6) ------------------------------------------ STRATEGY (23.6, GoF, p. 315ff) Problem: How to allow related algorithms to vary independently from clients that use them? How to support "pluggable" code? How to hide data needed by algorithms from clients. How to avoid lots of conditional logic to select algorithms. Solution: Define each algorithm in a separate class, with a common interface. Document the common specification in the interface, but use underspecification, to allow for the variation. Example: Airline pricing schemes ------------------------------------------ ------------------------------------------ BACKGROUND |---------------------------------------| | Reservation | |---------------------------------------| | pricingStrategy: IPricingStrategy | | flights: List of Flight | | | |---------------------------------------| | getTotal(): Money | | getPreDiscountTotal() : Money | | | |---------------------------------------| public class Reservation { public Money getTotal() { return pricingStrategy.getTotal(this); } public Money getPreDiscountTotal() { Iterator i = flights.iterator(); Money tot = new Money(); while (i.hasNext()) { Flight f = (Flight) i.next(); tot = tot.add(f.getTotal()); } return tot; } ------------------------------------------ ------------------------------------------ THE STRATEGY PART |--------------------------------------| | <> | | IPricingStrategy | |--------------------------------------| |--------------------------------------| | getTotal(Reservation context): Money | |--------------------------------------| ------------------------------------------ 1. discussion Where is the pricing strategy work really being done? a. creation How should the pricing strategy objects be created? b. data for strategies 2. related patterns What patterns are related? F. Composite and other design principles (23.7) ------------------------------------------ COMPOSITE (23.7) Problem: how to treat a group of objects in the same way as a single objects are treated? Solution: Define an interface that both the composite and atomic objects implement. Record the common specification in this interface. Example: ------------------------------------------ ------------------------------------------ JAVA CODE import java.util.Vector; public class CompositePricingStrategy implements IPricingStrategy { public void add(PricingStrategy ps) { pricingStrategies.add(ps); } protected Vector pricingStrategies = new Vector(); } ------------------------------------------ ------------------------------------------ A SAMPLE CONCRETE STRATEGY import java.util.*; public class BestForCustomerPricingStrategy extends CompositePricingStrategy { public Money getTotal(Reservation context) { Ierator i = pricingStrategies.iterator(); IPricingStrategy ps; Money lowest = new Money(Float.POSITIVE_INFINITY); while (i.hasNext()) { ps = (IPricingStrategy) i.next(); price = ps.getTotal(context); if (price.compareTo(lowest) < 0) { lowest = price; } } return lowest; } } ------------------------------------------ 1. discussion a. inheritance and abstract classes What is the difference between an abstract class and an interface? in the implementation of this last pricing strategy, where does the field (data member) pricingStrategies come from? How do pricing strategies get added to the composite? b. passing aggregate objects as parameters Why pass Reservation objects to the pricing strategy, instead of just passing the collection of flights? c. creation of multiple strategies at what point in making a reservation can we discover new pricing strategies that would apply to the reservation? What would a use case for getting the senior citizen discounts pricing strategies created Work through that design (see figures 23.17-18) d. other uses of the composite pattern creating macros of commands, which are just composites of commands e. exercises Q: how would you handle getting 25 percent off the cost of a reservation for taking a business class flight on Tuesday? How would you handle a buy one get one free sale? G. Facade (23.8) why can't we just use the strategy pattern? ------------------------------------------ FACADE (23.8) Problem: How to minimize coupling between subsystems? How to provide a higher-level interface that makes a subsystem easier to use? How to provide a simple default view of a subsystem? Solution: Define a single object, the facade, that has responsibility for interacting with the rest of the system. ------------------------------------------ is a handler a kind of facade object? ------------------------------------------ EXAMPLE public class Compiler { public Compiler() { ... } void compile (StringBuffer prog) { /* ... do all the work... */ } } ------------------------------------------ 1. discussion how many copies of the facade object do we need? do you really have to hide th other classes behind the facade? what's the impact of this pattern on coupling? in a design that uses patterns, we tend to have lots of small classes. How does this affect clients? 2. related patterns what's the difference between adapter and this pattern? H. Observer or Publish-Subscribe or Delegation Event Model (23.9) Why not just have the classes in the domain logic layer directly call the classes in the user interface layer? ------------------------------------------ OBSERVER OR PUBLISH-SUBSCRIBE (23.9) problem: How to allow events (state changes) to affect other objects, without coupling to them? How to support a one-to-many dependency between objects? How to separate layers that must talk to each other? Roles: A *publisher* or *subject* notifies other objects of an event. A *subscriber* or *observer* wishes to be notified of events. ------------------------------------------ In our Reservation/UI example, which is the publisher? Which is the subscriber? ------------------------------------------ SOLUTION Solution: Define an "Observer" or "Listener" interface, which documents the kinds of events and the information they carry. Subscribers/Observers implement the listener interface. Publishers have methods to dynamically register subscribers. Publishers notify all registered subscribers when an event occurs. ------------------------------------------ Can there be more than one subscriber? Zero? ------------------------------------------ DELEGATION EVENT MODEL IN JAVA |---------------------------------------| | Reservation | |---------------------------------------| | | |---------------------------------------| | | | addPropertyListener( | | IPropertyListener lis) | |#publishPropertyEvent(PropertyEvent e) | | | | enterFlight(Flight f) | | setTotal(Money newTotal) | | | |---------------------------------------| | 1 | | Property-Listeners | v * |---------------------------------------| | <> | | IPropertyListener | |---------------------------------------| |---------------------------------------| | onPropertyEvent(PropertyEvent e) | |---------------------------------------| |---------------------------------------| | PropertyEvent | |---------------------------------------| | source: Object | | name: String | | oldValue: Object | | newValue: Object | |---------------------------------------| | | |---------------------------------------| ------------------------------------------ ------------------------------------------ CODING IN RESERVATION import java.util.*; public class Reservation { public void enterFlight(Flight f) { flights.add(f); publishPropertyEvent( new PropertyEvent(this, "enterFlight", null, f)); } public void addPropertyListener( IPropertyListener lis) { subscribers.add(lis); } private Vector subscribers = new Vector(); protected void publishPropertyEvent( PropertyEvent e) { Iterator i = subscribers.iterator(); while (i.hasNext()) { IPropertyListener lis = (IPropertyListener) i.next(); lis.onPropertyEvent(e); } } } ------------------------------------------ 1. discussion a. many subscribers are possible b. unsubscribing In some applications in the subscribers will want to unsubscribe themselves dynamically. How would you support that? c. event terminology d. not just for connecting user interfaces and models 2. related patterns Why do want to protect against variation in the user interface?