Chapter 23 Strategy Summary prepared by Kirk Scott 1 2 3 Design Patterns in Java Chapter 23 Strategy Summary prepared by Kirk Scott 4 The Introduction Before the Introduction • In general, a single strategy might be thought of as an algorithm or an operation • In the context of the Strategy design pattern, the idea is that there are multiple approaches to doing something, depending on certain conditions or context • The Strategy design pattern, then, depends on picking the approach or picking the strategy 5 • The purpose of the Strategy design pattern is to separate the implementations of different strategies from each other • It also separates the code for picking the strategy from the strategy implementations 6 • The pattern defines a single interface for all strategies • The separate strategies are implemented with a method of the same name in each of the classes implementing the interface 7 • Which strategy is used will depend on what kind of object the method implementing the strategy is called on • The intent of the pattern is realized through an interface and depends on polymorphism and dynamic binding 8 Book Definition of Pattern • Book definition: • The intent of Strategy is to encapsulate alternative approaches, or strategies, in separate classes that each implement a common operation. 9 Modeling Strategies • Like with the previous chapters, and others, the book illustrates the Strategy design pattern in the following way: • It develops an example with multiple strategies that doesn’t use the Strategy design pattern • It then refactors the example using the Strategy design pattern 10 Example Scenario • When a potential customer calls in, interested in buying fireworks, there is software which will make a recommendation or suggestion • There are several different ways a recommendation can be made 11 • Ways of recommending a purchase in this scenario: • Recommend a particular firework that is being promoted • Use a piece of software, Rel8 • Use another piece of software, LikeMyStuff • Use a default recommendation option 12 Promoted Firework • There is nothing special about this option • If the company is promoting a firework, recommend it 13 Rel8 • Rel8 relies on a customer’s already being registered • During registration the customer specifies preferences in entertainment and fireworks • Rel8 makes a suggestion based on the similarity of the customer to other customers (presumably suggesting something that similar customers have tended to buy) • If the customer isn’t registered, Rel8 can’t be used 14 LikeMyStuff • LikeMyStuff doesn’t rely on pre-registration, but it does rely on customer information • The idea is that it will make a recommendation based on a profile of recent purchases by the customer • If not enough data can be obtained to form the profile, then LikeMyStuff can’t be used 15 The Default Option • This is the default: • If none of the previous options applies, then a firework is suggested at random 16 UML for the Scenario • The UML diagram on the following overhead shows the classes involved in the design as described so far • Appendix D on UML clarifies the notation: • “Use a dashed arrow between classes to show a dependency that does not use an object reference. For example, the Customer class relies on a static method from the LikeMyStuff recommendation engine.” 17 18 The getRecommended() Method • Viewing the scenario from the top down, what you have is this: • The Customer class has a getRecommended() method in it • This method consists of if/else code which chooses one of the strategies, whether to do a promotion, or to use Rel8, LikeMyStuff, or the default 19 Doing a Promotion • If there is a promotion underway, the first part of the logic of getRecommended() deals with that case • The logic for doing a promotion consists of looking up the contents of a file named strategy.dat in a directory named config • If there is such a file, its contents should look something like this: promote=JSquirrel 20 • The basic idea is that if the data file is not empty, the firework it contains is returned • If its contents come up null you go on to the next option • Also, if the file read doesn’t work, you don’t do anything in the catch block, you just continue on to the other options 21 Using Rel8 • The Rel8 class has a method advise() • getRecommended() wraps a call to advise() if the Rel8 strategy is selected • The call looks like this: • if(isRegistered()) • return (Firework) Rel8.advise(this); • “this” is the customer, and Rel8 relies entirely on the information contained in the registered customer object 22 Using LikeMyStuff • The LikeMyStuff class has a suggest() method • getRecommended() wraps a call to suggest() if the LikeMyStuff strategy is selected • The call looks like this: • if(spendingSince(cal.getTime()) > 1000) • return (Firework) LikeMyStuff.suggest(this); 23 • spendingSince() is called on the implicit parameter, customer • cal in the parameter refers to an instance of Calendar • getTime() specifies a recent period of time • “this” is the customer, which is sent as a parameter to suggest() 24 • suggest() relies on a database of recent purchases by that customer • The idea is that if the customer has recently spent $1,000, those purchases provide the basis for a recommendation 25 Doing the Default • The Firework class has a getRandom() method, so if all else fails, getRecommended() wraps a call to that method 26 The Code for getRecommended() in the Customer Class • The code for getRecommended() is shown on the following overheads • It is a collection of if statements. • It is unfortunate that it is not organized as a sequence of if/else if’s. 27 The Code for getRecommended() • • • • • • • • • • • • • • public Firework getRecommended() { // if promoting a particular firework, return it try { Properties p = new Properties(); p.load(ClassLoader.getSystemResourceAsStream(“config/strategy.dat”)); String promotedName = p.getProperty(“promote”); if(promotedName != null) { Firework f = Firework.lookup(promotedName); if(f != null) return f; } 28 • • • • • • • • • • catch(Exception ignored) { // If resource is missing or it failed to load, // fall through to the next approach. } // if registered, compare to other customers if(isRegistered()) { return (Firework) Rel8.advise(this); } 29 • • • • • // check spending over the last year Calendar cal = Calendar.getInstance(); cal.add(Calendar.YEAR, -1); if(spendingSince(cal.getTime()) > 1000) return (Firework) LikeMyStuff.suggest(this); • • • // oh well! return Firework.getRandom(); } 30 What’s Wrong with the Initial Design • The book identifies two basic problems with the getRecommended() method as given: • It’s too long • It combines both selecting a strategy and executing it 31 • This is actually one of the high points of the book • It explains that you know that the method is too long because you need to put comments in it • “Short methods are easy to understand, seldom need explanation…” 32 Comments Are Bad… • Finally, what every student always knew: Comments are bad… • More accurately, you might facetiously say that code which requires comments is bad. • The book doesn’t say that putting a comment at the beginning for the whole method is bad. • A useful observation might be that a method should be short and sweet enough that it doesn’t need internal commenting. 33 Refactoring to the Strategy Pattern • Applying the Strategy design pattern involves three things: • 1. Creating an interface that defines the strategic operation • 2. Writing classes that implement the interface and embody each of the different strategies • 3. Refactoring the code to select and use an instance of the right strategy class 34 The Interface • 1. The interface for this example will be named Advisor • The interface requires the implementation of a recommend() method • The recommend() method will take a customer as a parameter • It will return a firework • A UML diagram of the interface is given on the next overhead 35 36 The Implementing Classes • 2. The next step is to write the classes that implement the interface and embody each of the different strategies • These classes will have to implement the recommend() method 37 • The book does the refactoring in part with challenges • As usual, it’s easiest to just look at the solutions • The UML diagram on the following overhead shows: – A new Customer class making use of an Advisor interface – 4 classes which implement the interface and embody the 4 strategies 38 Solution 23.1 39 The Implementing Classes • The PromotionAdvisor and RandomAdvisor class names should be self-explanatory • GroupAdvisor refers to the use of Rel8 • ItemAdvisor refers to the use of LikeMyStuff • The implementations of the recommend() method for these classes will wrap a call to the static methods of Rel8 and LikeMyStuff • An expanded UML diagram for these two classes is given on the next overhead 40 41 Making Instances of the Implementing Classes • An interface can’t define static methods • An interface defines what the book calls “object methods”—methods that are called on objects • That means that client code will have to make instances of GroupAdvisor and ItemAdvisor • The recommend() method will be called on these objects 42 • Only one instance each of GroupAdvisor and ItemAdvisor are needed • In the refactored design, these instances will be static objects in the Customer class • So the advisor objects will be “singleton like” • There won’t be an instance of each kind of advisor for each customer 43 • Even though the recommend() method isn’t a static method, it more or less acts like one • If there is only one advisor object, then there is the one recommend() method that can be called on that object • The recommend() method does something for customers • But it does so by taking the customer as an explicit parameter rather than being called on the customer 44 Code for the recommend() Method in the GroupAdvisor Class • This is the recommend() method in the GroupAdvisor class: • public Firework recommend(Customer c) • { • return (Firework) Rel8.advise(c); • } • It wraps a call to the advise() method of Rel8 • In essence, the call is adapted to the recommend() interface of Advisor 45 Code for the recommend() Method in the ItemAdvisor Class • The code for the recommend() method in the ItemAdvisor class is analogous. • The book doesn’t give it and it doesn’t even bother to give it as a challenge. • It should be straightforward to write that method. 46 • Challenge 23.2 • “In addition to Strategy, what pattern appears in the GroupAdvisor and ItemAdvisor classes?” • [The answer to this was given away in the last remark about the recommend() code in GroupAdvisor.] 47 • Solution 23.2 • “The GroupAdvisor and ItemAdvisor classes are instances of Adapter, providing the interface a client expects, using the services of a class with a different interface.” 48 Code for the recommend() Method in the PromotionAdvisor Class • A PromotionAdvisor class is also needed, with a recommend() method • On the one hand, promotion should be a simple case • On the other hand, the book puts a lot of detail into the implementation 49 • Most of the logic of the original code is moved into the constructor for the new class • If a promotion is on, then the promoted instance variable of the class is initialized • In addition to the recommend() method, there is a hasItem() method which can be called to see whether a promoted item is available 50 • The book’s implementation makes use of class loading logic • This requires try/catch blocks • The details of this technique will not be covered since they are extraneous to the design pattern • The code is shown on the following overheads 51 • • • • • • • • • • • • • • • • • • • public class PromotionAdvisor implements Advisor { private Firework promoted; public PromotionAdvisor() { try { Properties p = new Properties(); p.load(ClassLoader.getSystemResourceAsStream("config/strategy.dat")); String promotedFireworkName = p.getProperty("promote"); if (promotedFireworkName != null) promoted = Firework.lookup(promotedFireworkName); } catch (Exception ignored) { // Resource not found or failed to load promoted = null; } } 52 • • • • • • • • • } public boolean hasItem() { return promoted != null; } public Firework recommend(Customer c) { return promoted; } 53 Code for the recommend() Method in the RandomAdvisor Class • The RandomAdvisor class is simple • Its code is shown on the following overhead 54 • public class RandomAdvisor implements Advisor • { • public Firework recommend(Customer c) • { • return Firework.getRandom(); • } • } 55 Refactoring the Customer Class to Use the Interface 56 Creating the Advisor Objects • A single instance of each kind of advisor is created in the new Customer2 class • For any given customer, recommend() may be called on one of these advisor objects • The code for the new customer class, Customer2, begins by creating these objects • This code is shown on the following overhead 57 • private static PromotionAdvisor promotionAdvisor = • new PromotionAdvisor(); • private static GroupAdvisor groupAdvisor = • new GroupAdvisor(); • private static ItemAdvisor itemAdvisor = • new ItemAdvisor(); • private static RandomAdvisor randomAdvisor = • new RandomAdvisor(); 58 Implementing the getAdvisor() Method • Customer2 contains a method named getAdvisor() for picking which kind of advisor to use • The original design had if/else statements in which different strategies were called • In the new design you don’t eliminate if/else statements, but they appear in a different place 59 • In the new design the if/else logic is implemented in the getAdvisor() method • getAdvisor() returns an advisor object • Polymorphism and dynamic binding determine which strategy is used when recommend() is called on the advisor object 60 How the getAdvisor() Method Works • Recall that getAdvisor() is in the Customer2 class • Its implementation includes a call to hasItem(), mentioned earlier to check for a promotion • Its implementation also includes calls to isRegistered() and isBigSpender() on the implicit customer parameter to determine if the advisor should be Rel8 or LikeMyStuff 61 • getAdvisor() does lazy initialization of the advisor • The value of the advisor instance variable is only set at the time the getAdvisor() method is called • It’s not lazy construction because one instance of each kind of advisor has already been created at the top of Customer2 • The code is shown on the following overhead 62 • • • • • • • • • • • • • • • private Advisor getAdvisor() { if (advisor == null) { if (promotionAdvisor.hasItem()) advisor = promotionAdvisor; else if (isRegistered()) advisor = groupAdvisor; else if (isBigSpender()) advisor = itemAdvisor; else advisor = randomAdvisor; } return advisor; } 63 The getRecommended() Method in Customer2 • Challenge 23.3 • “Write the new code for Customer.getRecommended().” • Comment mode on: • It may be helpful to step back and review where we’re at in the redesign • The UML for the redesign is repeated on the following overhead 64 65 • We’ve got the 4 advisor classes • We’ve got the getAdvisor() method • The final ingredient of the new design is the getRecommended() method in the Customer2 class • This code should rely on the advisor for the customer, and a call to the recommend() method on that advisor • The code is shown on the following overhead 66 Solution 23.3 • Your code should look something like: • public Firework getRecommended() • { • return getAdvisor().recommend(this); • } 67 68 The End 69
© Copyright 2025 Paperzz