500 likes | 632 Views
Objectives. Apply GRASP and GoF design patterns to the design of the POS case study
E N D
Objectives • Apply GRASP and GoF design patterns to the design of the POS case study • In this part, we will explores object design for use-case realizations for the POS case study, which tackles support for external third-party services whose interfaces may vary, more complex product pricing rules, and pluggable business rules
The Gang-of-Four Patterns • In practice, perhaps approximately 15 of these 23 patterns are frequently used
The Gang-of-Four Patterns (Continue) • Purpose – reflects what a pattern does • Creational • Concerns process of object creation • Structural • Deals with composition of classes or objects • Behavioral • Characterizes ways in which classes or objects interact and distribute responsibility
The Gang-of-Four Patterns (Continue) • Scope – specifies whether pattern applies to classes or objects • Class • Deals with relationships established via classes and their subclasses, through inheritance • They are static, fixed at compile time • Object • Deals with object relationships • Can be changed at run time (hence, more dynamic)
Case Study: Applying Adapter in POS • Facts: The POS system needs to support several kinds of external third-party services • including • tax calculators • credit authorization services • inventory systems • accounting systems • Each has a different API • Solution: • Add a level of indirection with objects that adapt the varying external interfaces to a consistent interface used within the application
Discussion - Insight of Adapter • The Adapter pattern is a specialization of Polymorphism, Indirection, and Protected Variations
Update Domain Model if Necessary • It is normal and common to discover domain concepts and refined understanding of the requirements during design when there is a newly created software class in the Design Model
Problem • The adapter raises a new problem in the design: In the prior Adapter pattern solution for external services with varying interfaces, • who creates the adapters? • how to determine which class of adapter to create, such as TaxMaster-Adapter or GoodAsGoldTaxProAdapter • Solution 1: created by domain object If some domain object creates them, the responsibilities of the domain object are going beyond pure application logic • Solution 2: by Factory
GoF Pattern - Factory • Context Problem • Who should be responsible for creating objects when there are special considerations, such as complex creation logic, a desire to separate the creation responsibilities for better cohesion, and so forth? • Solution • Create a Pure Fabrication object called a Factory that handles the creation
Case Study: Applying Factory in POS • How do we solve the problem raised earlier? • Solution: by Factory (Notice data-driven design)
Benefits of Factory • Separate the responsibility of complex creation into cohesive helper objects. • Hide potentially complex creation logic. • Allow introduction of performance-enhancing memory management strategies, such as object caching or recycling
New Problem with the Factory Solution • The ServicesFactoryraises another new problem in the design: who creates the factory itself, and how is it accessed? • Some considerations of this problem • First, observe that only one instance of the factory is needed within the process • Second, quick reflection suggests that the methods of this factory may need to be called from various places in the code • Solution 1: Pass the ServicesFactoryinstance as a parameter around • Solution 2: By the Singletonpattern
Gof Pattern: Singleton • Context Problem Exactly one instance of a class is allowed—it is a "singleton“. Objects need a global and single point of access • Solution Define a static method of the class that returns the singleton
Case Study: Applying Singleton in POS • Design Question: Who creates the ServicesFactory ?
Continue – How to Call Singleton? public class Register { public void initialize() { ... do some work ... // accessing the singleton Factory via the getlnstance call accountingAdapter = ServicesFactory.getlnstance().getAccountingAdapter(); ... do some work ... } // other methods... }
Implementation and Design Issues in Singleton • In multi-threaded applications, require thread concurrency control public static synchronized ServicesFactory getlnstance() { if ( instance == null ) { // critical section if multithreaded application instance = new ServicesFactory(); } return instance; }
Discussion - Static Method Vs Instance • What if we add a static method called getAccountingAdapter to ServicesFactory rather than calling getInstance() ? • Why we perfer the singleton method: • Instance-side methods permit subclassing and refinement of the singleton class into subclasses; static methods are not polymorphic (virtual) • Most object-oriented remote communication mechanisms (for example, Java's RMI) only support remote-enabling of instance methods, not static Methods • A class is not always a singleton in all application contexts. In application X, it may be a singleton, but it may be a "multi-ton" in application Y. Thus, the instance-side solution offers flexibility
Gof Pattern - Strategy • Context / Problem How to design for varying, but related, algorithms or policies? How to design for the ability to change these algorithms or policies? • Solution Define each algorithm/policy/strategy in a separate class, with a common interface
Case Study – Applying Strategy in POS • Design Question: • How to provide more complex pricing logic, such as a store-wide discount for the day, senior citizen discounts, and so forth • Facts: • The pricing strategy (which may also be called a rule, policy, or algorithm) for a sale can vary • Solution: • Since the behavior of pricing varies by the strategy (or algorithm), we create multiple SalePricingStrategyclasses, each with a polymorphic getTotalmethod
Case Study: Who Should Create Strategy? • Design Question: Who should create the strategy? • Solution: Apply the Factory pattern again: a PricingStrategyFactorycan be responsible for creating all strategies.
Continue • Because of the frequently changing pricing policy (it could be every hour), it is notdesirable to cache the created strategy instance in a field of the PricingStrategyFactory, but rather to re-create one each time • By reading the external property for its class name, and then instantiating the strategy
Discussion - Reading the Percentage Value • Finally, a design problem that has been ignored - how to find the different values for the percentage or absolute discounts Considerations: • Percentage values can be stored in a relational database, • so they can be easily changed • Then, what object will read them and ensure they are assigned to the strategy? • A reasonable choice is the Strategy-Factoryitself, since it is creating the pricing strategy, and can know which percentage to read from a data store
Insight of Strategy • Strategy is based on Polymorphism, and provides Protected Variations with respect to changing algorithms. • Strategies are often created by a Factory
Case Study: How do we handle multiple/conflicting pricing policies in POS ? • For example, suppose a store has the following policies in effect today (Monday): • 20% senior discount policy • preferred customer discount of 15% off sales over $400 • on Monday, there is $50 off purchases over $500 • buy 1 Hamburg and get 1 free What pricing policy should be applied? • Design Question: Is there a way to change the design so that the Saleobject does not know if it is dealing with one or many pricing strategies, and also offer a design for the conflict resolution? Answer: Yes, with the Composite pattern
Gof Pattern: Composite • Context / Problem • How to treat a group or composition structure of objects the same way (polymorphically) as a non-composite (atomic) object? • Solution • Define classes for composite and atomic objects so that they implement the same interface
Implementation // superclass so all subclasses can inherit a List of strategies public abstract class CompositePricingStrategy implements ISalePricingStrategy { protected List pricingStrategies = new ArrayList(); public add( ISalePricingStrategy s ) { pricingStrategies.add( s ); } public abstract Money getTotal( Sale sale ); } // end of class // a Composite Strategy that returns the lowest total of its inner SalePricingStrategies public class CompositeBestForCustomerPricingStrategy extends CompositePricingStrategy { public Money getTotal( Sale sale ) { Money lowestTotal = new Money( Integer.MAX_VALUE ); // iterate over all the inner strategies for( Iterator i = pricingStrategies.iterator( ) ; i.hasNextO; ) { ISalePricingStrategy strategy = (ISalePricingStrategy)i.next(); Money total = strategy.getTotal{ sale ); lowestTotal = total.min( lowestTotal ); } return lowestTotal; } } // end of class
Implementation of customer discount at the front end • Use Case UC1: Process Sale Extensions (or Alternative Flows): 5b. Customer says they are eligible for a discount (e.g., employee, preferred customer) • 1. Cashier signals discount request • 2. Cashier enters Customer identification • 3. System presents discount total, based on discount rules
Gof Pattern: Observer (Publish-Subscribe) • Context / Problem • You need to notify a varying list of objects that an event has occurred • Solution • Observers delegate a varying list of objects that an event has occurred • Intent • Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically • The subject knows it observers because the observers register with it. The subject must notify the observers when the event occurs. The observers are responsible both for registering with the subject and for getting the information from the subject when notified.
Implementation of Observer • Have objects (Observers) that want to know when an event happens attach themselves to another object (Subject) that is watching for the event occur or that triggers the event itself. • When the event occurs, the subject tells the observers that it has occurred. • The Adapter pattern is sometimes needed to be able to implement the Observer interface for all the observer-type objects.
Another Example of Observer • As another example, the following illustrates an AlarmClock, which is a publisher of alarm events and various subscribers • This example is illustrative in that it emphasizes that many classes can implement the AlarmListener interface, many objects can simultaneously be registered listeners, and all can react to the "alarm event" in their own unique way • One publisher instance could have from zero to many registered subscribers. For example, one instance of an AlarmClock could have three registered AlarmWindows, four Beepers, and one ReliabilityWatchDog. • When an alarm event happens, all these AlarmListeners are notified via an onAlarmEvent
Implementation • Events In both the Java and C# .NET implementations of Observer, an "event" is communicated via a regular message, such as onPropertyEvent. The event is formally defined as a class, and filled with appropriate event data. The event is then passed as a parameter in the event message. For example: class PropertyEvent extends Event { private Object sourceOfEvent; private String propertyName; private Object oldValue; private Object newValue; //... } class Sale { private void publishPropertyEvent( String name, Object old, Object new ) { PropertyEvent evt = new PropertyEvent( this, "sale.total", old, new); for each AlarmListener al in alarmListeners al.onPropertyEvent( evt ); } //. . . }