ObjectOriented Analysis and Design CHAPTER 26 GOF DESIGN
Object-Oriented Analysis and Design CHAPTER 26: GOF DESIGN PATTERNS 1
What will we learn? We will get an introduction to the Gang of Four (Go. F) Patterns We will see how the GRASP principles are a generalization of other design patterns 2
Go. F Patterns - Introduction The Gang of Four (Go. F) patterns were introduced in the book Design Patterns There are 23 in all; we will only study a few of them here Perhaps 15 or so are commonly used Note the original book assumes a knowledge of C++ and smalltalk; we will not assume that here 3
Go. F Patterns - Adapter The first Go. F pattern we will explore is called Adapter The GRASP Polymorphism principle is a generalized version of Adapter Recall that the Polymorphism principle allowed us to define abstract classes or interfaces to handle various external objects that had similar but differing interfaces Problem: How to resolve incompatible interfaces, or provide a stable interface to similar components with different interfaces? Solution: Convert the original interface of a component into another interface, through an intermediate adapter object. 4
Go. F Patterns - Adapter Recall the Next. Gen POS example; we need to support several kinds of external third party services, like tax calculators, authorization services, etc. Each of these has a different API (API: Application Progamming Interface, a set of externally facing public operations – methods - that are supported by the entity exposing the API). The solution is to add a level of indirection with a new class that “adapts” the various APIs or interfaces into one interface that can be used by the objects in our system Note that the Adapter class is not abstract – it is instantiated, and there is an adapter class for each of the various external services or systems being “adapted” The Adapter class may, however, utilize a common interface that is used by the other objects in the system 5
6
Go. F Patterns - Adapter Notice the relationship to the GRASP principles: The Adapter uses the GRASP principles of Indirection, Pure Fabrication, and Polymorphism However, Adapter is a more concrete solution – it describes how to solve the problem with a specific class construction Guideline: If you are creating an Adapter class, always a good idea to include the word “Adapter” in the class name – helps identify the class in the design model SAPAccounting. Adapter When adapters are created for resources, the Go. F pattern is also called a Façade pattern (discussed later) 7
8
Pattern Overload We noted that Adapter can be considered a variation of Pure Fabrication, Indirection, and Polymorphism There are literally hundreds of patterns that have been identified We will try to concentrate on the underlying basic themes, which are often common to many of the patterns So while Adapter is a pattern that demonstrates Polymorphism, Indirection, and Pure Fabrication, these principles are ways to supporting Low Coupling and High Cohesion, all of which support Protected Variation. 9
The Factory Pattern Also called Simple Factory or Concrete Factory. Technically not a Go. F Pattern, but widely used. Go. F has a pattern called Abstract Factory which is related Here is the problem: In situations such as the case we just saw when Adapter classes are used, who creates the Adapters? If there are several possible adapters in the design, how does the system determine which adapter to create? Note that this is not part of the usual application logic that would be known by domain objects Related more to software connectivity (to external systems, e. g. ) Always a good idea to design with the intent to modularize or separate out the various concerns of the system – leads to high cohesion of the objects So using Register to create the Adapters is probably not a good idea 10
The Factory Pattern A common solution is to use what is called a Factory Pattern This is a special case of Pure Fabrication: We define a class with the sole purpose to create objects, an “object factory” This helps keep the other objects (like domain objects) more cohesive Helps to hide the logic of creating the classes, which may be complex for some external systems We can also use smarter algorithms for creating the classes to enhance code performance (memory saving, object recycling, etc. ) Problem: Who should be responsible for creating objects when there are special considerations, such as a complex creation logic, a need to separate creation responsibilities, etc. ? Solution: Create a Pure Fabrication object called a Factory that handles the creation. 11
12
The Singleton Pattern Question – who creates the Factory itself, and how is it accessed? Notice that we really only need one Factory, for example to create the Adapter classes for the services that we saw in the last example. Also, we note that the methods of the Factory (which create and return the instances of the Adapters) may be called from numerous places in the code, so this single Factory instance needs to visible to any objects in the design We could create the Factory instance and then whenever an object needs access to it make sure that object is instantiated with a reference to the Factory, but this is clumsy and inconvenient Better solution is to use a Singleton pattern … this pattern provides a way to support global visibility or a single access point for an object 13
The Singleton Pattern Problem: Exactly one instance of a class is allowed – it is a “singleton” – and objects need a global and single point of access Solution: Define a static method of the class that returns the singleton. To implement this, we frequently use a static variable and a static method. Recall static methods may be invoked even if an instance of the class has not been created, but they cannot access instance-side variables (only static variables) To solve the Singleton problem, we can create a static variable in the Factory that refers to the instance of the Factory, and then a static method that instantiates this instance and returns it This way, other objects can create the Singleton (if it does not already exist) and use the Factory to access the Adapters it is responsible for creating (see next slide) 14
15
The Singleton Pattern The static method can be accessed by any object that has visibility to the Services. Factory class: public class Register { public void initialize() { … accounting. Adapter = Services. Factory. get. Instance(). get. Accounting. Adapter(); … } } Note we access the static method through the class name, not a specific instance. 16
The Singleton Pattern Note the code for the get. Instance() method first checks to see if the instance already exists, and if not, it creates the instance of the Factory This is known as lazy initialization, but it is more efficient than just creating the instance in the attribute definition (eager instantiation) Lazy instantiation is usually preferred – more robust and efficient Note the use of the synchronized static method – this locks the creation of the Factory instance in the case of multi-threaded applications, to avoid a conflict Generally not recommended to just make all methods static in the Singleton – this could limit use of the class (it cannot be sub-classed, for example) A class may be a singleton in one application but not in another Static methods are not polymorphic 17
The Singleton Pattern Return to the POS example – we have several accounting systems that we can interface to, we need to log the Sale on the SAP system Note that (contrary to our earlier analysis), we are going to let the Register log the Sale, since it has direct knowledge of the Sale When the Register is created (by the Store), it will access the Services. Factory public static method to get Accounting. Adapter instance After the make. Payment() operation is handled, the Register will log the Sale (see next slide) 18
Sale 19
The Strategy Pattern This pattern addresses the design problem of how to handle complex logic, such as pricing algorithms, which may vary or change Consider a pricing policy which may be 10% for a certain time and then change on another day 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 This is another example of protected variations using polymorphism 20
The Strategy Pattern Example: Suppose in the POS case study we have a pricing strategy, which applies various discounts to the Sale total once the total has been computed. How do we build this into the design? It would be complicated to have the Sale object have a separate method for each type of pricing discount – this overloads the Sale object, leading to low cohesion Using the Strategy pattern, we should define separate classes for each price discount plan, and have Sale use these Each class can implement a common interface, and hence we can use polymorphism to define a get. Total() method for each pricing strategy class The Sale object can then use this interface to calculate the discount pricing (see next slide) 21
22
The Strategy Pattern The strategy object is usually attached to a context object, which is the object to which it will apply the algorithm In the above example, the context object is Sale The strategy object will help the context object implement some algorithm as the result of message or operation received, and it is common to have the polymorphic method (implemented by the strategy object) be the same name as the operation received by the context object (in this case, get. Total()) It is also common to pass a reference of the context object to the strategy object so the strategy object has parameter visibility to the context object Why not just past the calculated total in the next slide? 23
24
The Strategy Pattern It may seem unnecessarily complex to pass the context object to the strategy object (why not just pass the current calculated total, which the Sale knows, rather than pass the entire Sale? ) The algorithm implemented by the strategy object may be more complex (buy two, get one free, for example), so it may need more visibility into the context object Note that in implementation, the Sale needs attribute visibility of its strategy To implement the interface, in the Sale, a reference attribute is declared in terms of the interface ISale. Pricing. Strategy The method get. Total() can then be invoked through this variable. Who creates the actual pricing strategy class? A good job for a Factory class 25
26
The Strategy Pattern The Pricing. Strategy. Factory can be designed like we saw earlier: It is a singleton that creates a pricing strategy object, if one does not exist Can also be designed to update the pricing strategy object (replace it with a new one) The Sale can then use this pricing strategy object to calculate any discounts This is done through the interface ISale. Pricing. Strategy which the pricing strategy object extends “Declaring the pricing strategy in terms of the interface” – like type casting For an excellent review of these concepts for Java: http: //www. artima. com/objectsandjava/webuscript/Polymorphism. Interfaces 1. html 27
28
29
Takeaways from Chapter 26 We began to look at the Go. F patterns Adapter Factory Singleton Strategy Understand how these patterns work, and what problems they can be used to solve 30
Next … More Go. F Patterns, conclusion of Chapter 26 31
- Slides: 31