Strategy and Template Pattern

4,960 views

Published on

This is Class 2 on a 6 week course I taught on Software Design Patterns.

This course discusses Strategy and Template pattern.

Class based on "Head First Design Patterns."

Published in: Technology, Business
0 Comments
4 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
4,960
On SlideShare
0
From Embeds
0
Number of Embeds
37
Actions
Shares
0
Downloads
208
Comments
0
Likes
4
Embeds 0
No embeds

No notes for slide
  • 13 November 2008
  • A duck pond simulation game. Assume that there is a screen with a pond. There are buttons for Quack, Swim, Fly, etc. You can select a Duck and say “do this”. Or you can press a button and say “All Ducks Quack”. “All Ducks Swim”. “All Ducks Fly”, etc. Also, let’s say we have a Vision Statement from the Tech Lead: be able to deploy the application once…then apply patches when needed. This patches should not affect code all ready deployed..simply extend when we already have. This will simplify re-testing everything! 13 November 2008
  • Pg 2. Fly is added. This is a problem since not all Ducks fly. So if you add a new Duck to the pond, it will automatically fly when the user presses that “All Duck Fly” button. Pg 4 With Inheritance, we see that not all derived classes necessarily use the behavior of the base class (as requirements change). We could of course use more inheritance to abstract out different behaviors…but imagine what happens every time a requirements change happens? You may have to completely change your inheritance hierarchy and re-shift things around. It may be easier to just put stuff in the base class and then inherit, and then overwrite the behavior with null implementation. Inheritance implies a compile time decision. Note when happens with a DecoyDuck is added. You have to do a lot of maintenance here. 13 November 2008
  • Having Fly and Quack in the superclass didn’t make sense here. Now we learn it will be changing a lot in the future. A Duck may Fly one day and then the customer says it should not fly anymore. Also, the user at runtime wants to change the behavior. 13 November 2008
  • This is a very important principle. If you have an IF/ELSE block and you know that it will never never change, then it is probably good as-is. If you have an IF/ELSE and you know that there is a lot of potential change, then it is a good idea to extend. 13 November 2008
  • Part that varies: Flying and Quacking Part that is the same: Displaying, Swimming Let’s pull out Fly and Quack into their own separate objects. 13 November 2008
  • 13 November 2008
  • Note that FlyBehavior and QuackBehavior are Interfaces. They do not need to be. What if there was a lot of common behavior behind Flying and Quacking? You may want an abstract class here instead. The basic concept here is that there an abstraction here. This is a very important concept. Think about the Singleton example I gave last week. The Singleton was tied to a specific instance of Oracle database. If you were trying to create a Common Component for your Enterprise, that Singleton would be a bad example example since it was tied to Oracle. You probably would want an object (and perhaps not a Singleton) that is designed towards an abstraction of a database. In general, this concept is key for designing frameworks or other base classes. If you are creating a base class with a lot of sub-classes, look closely. Does the base class rely on concrete implementations or abstractions?? 13 November 2008
  • Pg 18 Step 1 – Abstract Duck. Note that is composed of Fly and Quack behavior. performFly and performQuack both delegate to these behaviors. Look at swim. This implies that all ducks swim. I would mark this method as final so that no one can override, else we have the same inheritance problem that we saw earlier. Look at display. It is abstract. Could we have used a Display behavior here instead?? Just like Fly and Quack??? Well, we knew that Fly and Quack will be changing a lot, so that is why we did not want to declare that behavior in the base class. Display, however, does not change. A MallardDuck will have a unique display (ie,. The image of the MallardDuck). So inheritance here is used and that is OK. Step 2 and 3 – Defines different behaviors. Note that these behaviors are a separate set of classes. There is no relation to Duck. Step 4 – what happens when mallard.performQuack() is called?? Duck.performQuack() is called It delegates to FlyBehavior.Fly(). In the case of Mallard, that is FlyWithWings.fly(). 13 November 2008
  • Step 1 – these can be called at any time to change behavior! Even though we had some behavior set up in the constructor. Step 2 – we create a new type of Duck Step 3 – we create a new type of behavior Step 4 – we create a new ModelDuck. We say fly. Then we dynamically change the behavior…and then say fly again. 13 November 2008
  • Big difference between Inheritance and Composition: With Inheritance, you are statically defining behavior at the time of coding. With Composition, you have the option to dynamically change behavior. The requirements can help dictate whether to use inheritance or composition. 13 November 2008
  • This does not mean that Inheritance is bad! 13 November 2008
  • Go through the lab together. 13 November 2008
  • 13 November 2008
  • You would have three classes: LogLevel1 LogLevel2 LogLevel3 Each class describes the log implementation. What is the requirement came that the log level must be changed dynamically by the user at runtime?? We would need to manage the correct LogLevel class I guess! Question 3a: Since we have already used inheritance, we really can’t use it again! We would have to use composition here…even if we don’t care about the runtime requirement. Each LogLevelXX class can use all possible behaviors of OpenFiles. If the log level does not need to change during runtime, then this implementation is fine. But if the Log Level also needs to vary during runtime, then this is not a good implementation. We would want to use composition to vary both OpenFiles and WriteLogMessage. 13 November 2008
  • Hint #1 : Create an interface that represents the variable behavior Hint #2 : Compose Logger with this variable behavior. Hint #3: Have Logger delegate to this behavior. 13 November 2008
  • 13 November 2008
  • What is wrong with this? If you add another logwriter, you need to make two changes: Add a new entry into LogWriterEnum You need to update the setLogWriter every time you change LogWriterEnum. We could have the function talk to another object outside of Logger completely. It could ask: Give me the LogWritter object after I give you this enum. By doing this, it keeps any changes that you need to update within Logger itself. 13 November 2008
  • Discuss SQL parser. I know there is change coming in different areas, for example: SQL Operator (AND, OR, etc) or how to parse a leaf node (EXISTS(XXX) or Column or Inner Query). I have coded this out into a separate class for easy extension. This also has a nice side-benefit, I could easily give the extended behavior to another developer and the developer doesn’t need to fully understand the infrastructure that code lives in. They could just focus on the new behavior. 13 November 2008
  • Not Shown: A Setter 13 November 2008
  • Functionally, we have not changed anything. We can use unit tests to verify. But what is wrong with the new DefaultBehavior() line? It violates the rule of programming to an interface. Alternative: create a getter(). If the strategy is null, then return a default implementation. 13 November 2008
  • The new() operator for creating a new strategy is now nowhere to be seen in this class. This implies that the Client must provide the implementation. Why is this important? Imagine again the Database Object that is written to use a generic Db objects. You put this in a common component. Then different implementations can call this code with the concrete object that is needed. Also useful for unit testing for injecting a mock object. Briefly discuss the problems with Singletons…you cannot access the private constructor very easily. This idea is the basis for Spring and similar frameworks. This example is simple. 13 November 2008
  • 4 steps. boilWater and pourInCup are exactly the same! The 2 nd and 4 th steps are not quite the same…but very similar. 13 November 2008
  • The code does have boilWater and pourInCup in common..so that is good. That code is encapsulated in one place. What happens if a fifth step is added? You would have to change both Coffee and Tea. Is this a good design? Note that the order that these methods are called are not enforced. You could easily change the order of the four functions of Tea! What happens if a new developer comes? Is it apparent that these are the correct order of steps? 13 November 2008
  • 13 November 2008
  • Strategy and Template Pattern

    1. 1. Design Patterns 05/27/10 Week 2: Strategy and Template Jonathan Simon [email_address]
    2. 2. Agenda <ul><li>Strategy Pattern </li></ul><ul><ul><li>SimUDuck Application (Head First) </li></ul></ul><ul><ul><li>Setting Behavior Dynamically </li></ul></ul><ul><ul><li>Favor Composition over Inheritance </li></ul></ul><ul><ul><li>Lab </li></ul></ul><ul><ul><li>Refactoring w/Strategy </li></ul></ul><ul><ul><li>Dependency Injection </li></ul></ul><ul><li>Template Pattern </li></ul><ul><ul><li>Hooks </li></ul></ul><ul><ul><li>Hollywood Principle </li></ul></ul>05/27/10
    3. 3. SimUDuck Application (Head First Design Patterns) <ul><li>pg 2 in book. </li></ul><ul><li>Duck can </li></ul><ul><ul><li>Quack </li></ul></ul><ul><ul><li>Swim </li></ul></ul><ul><ul><li>Display – handles visual display </li></ul></ul><ul><li>It is assumed that all Ducks have the same behavior for Quack and Swim. </li></ul>05/27/10
    4. 4. SimUDuck Application <ul><li>pg 3 </li></ul><ul><li>Now fly() behavior is added to the superclass. This is a new requirement! </li></ul><ul><ul><li>Problem since RubberDucks don’t fly! </li></ul></ul><ul><li>pg 4-5. How can this problem be solved? </li></ul><ul><ul><li>We could override fly() in RubberDuck to do nothing. </li></ul></ul><ul><ul><li>Also note that RubberDucks do not quack..they Squeak. </li></ul></ul><ul><li>What was good for reuse is bad for maintenance. </li></ul><ul><ul><li>Note: a change to the superclass unintentionally can affect all subclasses. You would have to inspect each subclass. </li></ul></ul>05/27/10
    5. 5. Using Interface <ul><li>pg 6 – we learn that the superclass flying and quacking behavior will constantly change (due to changing customer requirements) </li></ul><ul><ul><li>What would need to happen every time the behavior changed? </li></ul></ul><ul><li>Joe creates a Flyable and Quackable interface and applies it to the classes which has those behaviors. He pulls Fly and Quack out of the superclass. </li></ul><ul><ul><li>How will each Duck sublcass define those behaviors? </li></ul></ul><ul><ul><li>Could there be possible duplication of code? Ie, MallardDuck’s implementation of fly() versus RedheadDuck’s implementation. </li></ul></ul><ul><ul><li>What if there are different variations of flying? </li></ul></ul>05/27/10
    6. 6. The problem… <ul><li>Inheritance didn’t work </li></ul><ul><ul><li>Not all subclasses need to Fly or Quack. </li></ul></ul><ul><ul><li>Making a change to superclass will cause a maintenance headache. </li></ul></ul><ul><li>Flyable/Quackable interface didn’t work </li></ul><ul><ul><li>No code re-use among the different Fly/Quack behaviors. </li></ul></ul><ul><ul><li>If Fly changed, you would need to track down all subclasses that use the Flyable interface. </li></ul></ul><ul><li>Design Principle : “Identify the aspects of your application that vary and separate them from what stays the same.” </li></ul><ul><ul><li>You can alter or extend parts that vary without affecting the parts that are static. </li></ul></ul>05/27/10
    7. 7. Back to SimUDuck <ul><li>Which part varies? </li></ul><ul><li>What part does not? </li></ul><ul><li>How can we pull out the behavior that varies from the Duck class? </li></ul><ul><li>Also, how can we extend this behavior in the future without having to re-write existing code? </li></ul>05/27/10
    8. 8. Closer Look at Variable Behavior <ul><li>pg 13: Create two sets of classes (one for Flying and one for Quacking). These two sets of classes represents the behavior that varies. </li></ul><ul><li>We’d like the behaviors to be flexible, for example: </li></ul><ul><ul><li>Create a MallardDuck that can fly and quack. But then, by a simple code change, change the MallardDuck so that it cannot fly and that when it quacks, it’s actually the song “Gimme back that Filet-O-Fish.” </li></ul></ul><ul><li>We don’t want the Duck class to be tied to a specific behavior of Fly or Quack. What if the Duck class was tied to an abstraction of the behavior?? </li></ul>05/27/10
    9. 9. Design Principle #2 <ul><li>“ Program to an interface, not an implementation.” </li></ul><ul><ul><li>(I like “program to an abstraction, not an implementation.”) </li></ul></ul><ul><li>Define the interfaces FlyBehavior and QuackBehavior. The Duck class will be tied to these interfaces, but not a specific implementation. </li></ul><ul><li>Each Duck subclass will use a specific implementation of this behavior. </li></ul>05/27/10
    10. 10. Breaking it down… <ul><li>pg 13. FlyBehavior and QuackBehavior </li></ul><ul><ul><li>The FlyBehavior and QuackBehavior represents an abstraction of the flying and quacking. </li></ul></ul><ul><ul><li>FlyWithWings, FlyNoWay, Quack, etc are implementations of these behaviors. </li></ul></ul><ul><li>pg 15. The Duck class contains instance variables of FlyBehavior and QuackBehavior. </li></ul><ul><ul><li>Here is an example of programming to an abstraction. The Duck class (an abstraction itself) is composed of the abstraction of two behaviors. </li></ul></ul><ul><li>Pg 15. performQuack function() </li></ul><ul><ul><li>The operation of Quacking is delegated ! The Duck class does not do it itself. </li></ul></ul>05/27/10
    11. 11. Breaking it down… (cont) <ul><li>Pg 16. In the constructor of MallardDuck, the quack and fly behavior is set up. </li></ul><ul><ul><li>Note: On pg 17, the book admits that this constructor isn’t an ideal solution. This does violate the principle of “programming to an implementation” since we are tying the MallardDuck to a specific behavior. </li></ul></ul><ul><ul><li>However, this can be changed dynamically. </li></ul></ul><ul><li>pg 18 Testing the Duck Code </li></ul>05/27/10
    12. 12. Setting Behavior Dynamically <ul><li>pg 20. </li></ul><ul><li>1. Add two new methods to Duck class </li></ul><ul><li>2. ModelDuck is created with some default behavior. </li></ul><ul><li>3. Create a new fly behavior </li></ul><ul><li>4. Note that ModelDuck is created..then the behavior is changed. </li></ul>05/27/10
    13. 13. To Think About <ul><li>If we had used inheritance for Fly(), could you change the Fly behavior at runtime? </li></ul><ul><li>Look at the FlyBehavior classes…what if there is common functionality amongst all of the FlyBehavior classes? </li></ul><ul><ul><li>Instead of an interface, use an abstract class for FlyBehavior. </li></ul></ul><ul><ul><li>Think of this as a “family of algorithms” </li></ul></ul>05/27/10
    14. 14. Favor Composition over Inheritance <ul><li>Pg 23 </li></ul><ul><li>With Composition, we were able to dynamically change behavior at runtime. </li></ul><ul><li>With Inheritance, behavior was decided a compile time. You could not change at runtime when you use inheritance. </li></ul>05/27/10
    15. 15. The Strategy Pattern <ul><li>GoF Intent: “Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.” </li></ul>05/27/10
    16. 16. Lab <ul><li>public class Logger { </li></ul><ul><li>public void LogMessage(string message, int level) { </li></ul><ul><li>PrintSteam[] files = OpenFiles(); </li></ul><ul><li>WriteLogMessage(files, message, int level); </li></ul><ul><li>CloseFiles(intfiles); </li></ul><ul><li>} </li></ul><ul><li>private void WriteLogMessage(PrintStream[] files, </li></ul><ul><li>string message, int level) { </li></ul><ul><li>for (int i=0; i < files.Length; i++) { </li></ul><ul><li>if (level == 1) { </li></ul><ul><li>//Write error message to log file. </li></ul><ul><li> else if (level == 2) </li></ul><ul><li> //Write error message to log file + Log the </li></ul><ul><li> //UserName and IP address. </li></ul><ul><li>else if (level == 3) </li></ul><ul><li>//Write error message to log file. + Send email to </li></ul><ul><li> //administrator </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
    17. 17. Lab (cont) <ul><li>Question 1: Which part of the code varies? </li></ul><ul><li>Question 2: What part of the code does not vary? </li></ul><ul><li>Question 3: Using inheritance, describe how you could isolate the parts that vary. </li></ul><ul><li>Question 3a: (Continuing from Question 3 with inheritance) Let’s say we learn that the function OpenFiles has different behavior as well. This behavior is completely unrelated to the logging level. We start by creating if/else statements inside this function. We get in trouble from our manager for doing this since we know that there will be future change. Can we use inheritance to fix this problem? </li></ul>05/27/10
    18. 18. Lab (cont) <ul><li>Question 4: Using composition, design out how you would isolate the parts that vary. </li></ul><ul><li>Hint #1: Create an interface that represents the variable behavior </li></ul><ul><li>Hint #2: Compose Logger with this variable behavior. </li></ul><ul><li>Hint #3: Have Logger delegate to this behavior. </li></ul><ul><li>Question 4a: How can we change the logging level during runtime? </li></ul>05/27/10
    19. 19. Lab (cont) <ul><li>Question 4b: We get some flexibility by changing the behavior during runtime, but this also means that the person who is coding the Context object (i.e., Logger) must be aware of all possible behaviors. For example, to call setLogWriter here, you would need to be aware of LogWriter1 and LogWriter2. </li></ul><ul><li>Logger logger = new Logger() </li></ul><ul><li>Logger.setLogWriter(new LogWriter1()); </li></ul><ul><li>Logger.setLogWriter(new LogWriter2()); </li></ul><ul><li>Is there an alternative to sending an instance of LogWriter1 or LogWriter2, and yet achieve the same results? </li></ul>05/27/10
    20. 20. Lab (cont) <ul><li>Question 4c: Let’s say the Logger class contains the properties LoggedInUser and LastTimeLogged. We know that most of the log writers (LogWriter1, LogWriter2, etc) need access to these properties. What can we do? </li></ul><ul><li>Bonus Question: For a Logger implementation (such as LogWriter1), how many instances do we really need? </li></ul>05/27/10
    21. 21. Lab Answers <ul><li>Question 1 - In WriteLogMessage function, the if/else statement with the three different options. </li></ul><ul><li>Question 2: </li></ul><ul><ul><li>The three lines of code in function LogMessage </li></ul></ul><ul><ul><li>The for loop declaration in function WriteLogMessage </li></ul></ul>05/27/10
    22. 22. Lab Answers <ul><li>Question 3 </li></ul><ul><li>public abstract class Logger { </li></ul><ul><li>public void LogMessage(string message) {…} </li></ul><ul><li>private void WriteLogMessage(PrintStream[] files, string message) { </li></ul><ul><li>for (int i=0; i < files.Length; i++) { </li></ul><ul><li>WriteLogMessageImpl(PrintStream file, message); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>protected abstract WriteLogMessageImpl(PrintStream file, string msg); </li></ul><ul><li>} </li></ul>05/27/10 Question 3a:
    23. 23. Lab Answers <ul><li>Question 4 </li></ul><ul><li>public interface ILogWriter { </li></ul><ul><li>void Write(PrintStream file, string message); </li></ul><ul><li>} </li></ul><ul><li>public class Logger { </li></ul><ul><li> public ILogWriter logWriter; </li></ul><ul><li>private void WriteLogMessage(PrintStream[] files, string message) { </li></ul><ul><li>for (int i=0; i < files.Length; i++) { </li></ul><ul><li>logWriter.Write(file, message); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>05/27/10
    24. 24. Question 4a <ul><li>public void setLogWriter(ILogWriter writer) { </li></ul><ul><li>this.logWriter = writer; </li></ul><ul><li>} </li></ul>05/27/10
    25. 25. Question 4b <ul><li>public enum LogWriterEnum { </li></ul><ul><li>Simple, </li></ul><ul><li>Medium, </li></ul><ul><li>Hard </li></ul><ul><li>} </li></ul><ul><li>public void setLogWriter(LogWriterEnum logEnum) { </li></ul><ul><li> </li></ul><ul><li> if (logEnum == Simple) </li></ul><ul><li>this.logWriter = LogWriter1(); </li></ul><ul><li>else if (logEnum == Medium) </li></ul><ul><li>this.logWriter = LogWriter2(); </li></ul><ul><li>… </li></ul><ul><li>} </li></ul><ul><li>Alternative Design: </li></ul><ul><li>http://www.codeproject.com/KB/architecture/FactoryStrategyDesignPatt.aspx </li></ul>05/27/10 So what if we created an enum…
    26. 26. Question 4c <ul><li>Option 1 </li></ul><ul><li>public interface ILogWriter { </li></ul><ul><li>void Write(PrintStream file, string message, </li></ul><ul><li>string LoggedInUser, string LastTimeLogged ); </li></ul><ul><li>} </li></ul><ul><li>We can send the information in the method call itself. The disadvantage is that we may be sending more information than needed. </li></ul>05/27/10
    27. 27. Question 4c <ul><li>Option 2 – We can pass a reference of Context (Logger) itself </li></ul><ul><li>public interface ILogWriter { </li></ul><ul><li>void Write(PrintStream file, string message, Logger log ); </li></ul><ul><li>} </li></ul><ul><li>Advantage - The strategy can ask the Logger class for information that it needs. (and it may not need anything!) </li></ul><ul><li>Disadvantage – The strategy becomes more tightly coupled with the Context object. </li></ul>05/27/10
    28. 28. Bonus Question <ul><li>We can use Singletons! </li></ul><ul><li>Instead of this code: </li></ul><ul><li>Logger.setLogWriter(new LogWriter1()); </li></ul><ul><li>Logger.setLogWriter(new LogWriter2()); </li></ul><ul><li>We can use this code: </li></ul><ul><li>Logger logger = new Logger() </li></ul><ul><li>Logger.setLogWriter(LogWriter1.TheInstance); </li></ul><ul><li>Logger.setLogWriter(LogWriter2.TheInstance); </li></ul>05/27/10
    29. 29. Advantage: Unit Testing <ul><li>What if a new behavior is needed? </li></ul><ul><ul><li>Ie, new FlyBehavior, new QuackBehavior, new LogWriter, etc </li></ul></ul><ul><li>The new class can be created and unit tested on its own (in complete isolation of the infrastructure that uses it). </li></ul><ul><li>Another developer who does not know the “Big Picture” can easily be given direction to create the new behavior and unit test it. </li></ul>05/27/10
    30. 30. Refactoring w/Strategy <ul><ul><li>public class SomeClass() { </li></ul></ul><ul><ul><li>public void Function1() { </li></ul></ul><ul><ul><li>//section of code that does not vary </li></ul></ul><ul><ul><li>//The next line of code will vary </li></ul></ul><ul><ul><li> int i = (SOME_CONST*50)+100; </li></ul></ul><ul><ul><li> //section of code that does not vary </li></ul></ul><ul><ul><li>} </li></ul></ul><ul><ul><li>} </li></ul></ul>05/27/10 First question you need to ask yourself: use inheritance or composition?
    31. 31. Refactoring (cont) <ul><li>public interface IComputeStrategy { </li></ul><ul><li>int Compute(); </li></ul><ul><li>} </li></ul><ul><li>public class DefaultComputeStrategy implements IComputeStrategy { </li></ul><ul><li>public int Compute() { </li></ul><ul><li> return (SOME_CONST*50)+100; </li></ul><ul><li> } </li></ul><ul><li>} </li></ul>05/27/10
    32. 32. Refactoring (cont) <ul><li>public class SomeClass { </li></ul><ul><li>private IComputeStrategy strategy; </li></ul><ul><li>public SomeClass() { </li></ul><ul><li>this.strategy = new DefaultComputeStrategy(); </li></ul><ul><li>} </li></ul><ul><li>public void Function1() { </li></ul><ul><li>//section of code that does not vary </li></ul><ul><li> int i = strategy.Compute(); </li></ul><ul><li> //section of code that does not vary </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>05/27/10
    33. 33. Dependency Injection <ul><li>Technique for providing an external dependency to a software component. </li></ul><ul><li>In the Refactoring example, SomeClass is dependent upon a concrete implementation: </li></ul><ul><li>public SomeClass() { </li></ul><ul><li>this.strategy = new DefaultComputeStrategy(); </li></ul><ul><li>} </li></ul><ul><li>But what if we did this… </li></ul><ul><li>public SomeClass(IComputerStrategy strategy) { </li></ul><ul><li>this.strategy = strategy; </li></ul><ul><li>} </li></ul>05/27/10
    34. 34. Injection References <ul><li>http://martinfowler.com/articles/injection.html </li></ul><ul><li>http://msdn.microsoft.com/en-us/magazine/cc163739.aspx </li></ul>05/27/10
    35. 35. Summary <ul><li>Pattern Name – Strategy </li></ul><ul><li>Problem – Different algorithms will be appropriate at different times. Need a way for an object to use different algorithms at runtime. </li></ul><ul><li>Solution </li></ul><ul><ul><li>Define a family of classes that use the same interface </li></ul></ul><ul><ul><li>Provide a set method so that the behavior can be set (and changed) at runtime. </li></ul></ul><ul><li>Consequences </li></ul><ul><ul><li>Hierarchy of strategy classes </li></ul></ul><ul><ul><li>Alternative to subclassing </li></ul></ul><ul><ul><li>Eliminates conditional statements </li></ul></ul><ul><ul><li>Clients must be aware of strategies </li></ul></ul><ul><ul><li>Communication between Context and Strategy </li></ul></ul>05/27/10
    36. 36. Template <ul><li>pg 276 contains the requirement on brewing coffee and tea. </li></ul><ul><li>Look at code on pg 277 (Coffee) and pg 278 (Tea) </li></ul><ul><li>How many different steps are there? </li></ul><ul><li>What is similar? </li></ul><ul><li>What is different? </li></ul>05/27/10
    37. 37. Template <ul><li>Pg 280. </li></ul><ul><li>Coffee’s implementation of prepareRecipe() would have to call the following: </li></ul><ul><ul><li>boilWater </li></ul></ul><ul><ul><li>brewCoffeeGrinds </li></ul></ul><ul><ul><li>pourInCup </li></ul></ul><ul><ul><li>addSugarAndMilk </li></ul></ul><ul><li>Tea’s implementation of prepareRecipe() would have to call the following: </li></ul><ul><ul><li>boilWater </li></ul></ul><ul><ul><li>steepTeaBug </li></ul></ul><ul><ul><li>pourInCup </li></ul></ul><ul><ul><li>addLemon </li></ul></ul>05/27/10
    38. 38. Template <ul><li>Pg 282 </li></ul><ul><li>The second step of Coffee (brewCoffeeGrinds) and the second step of Tea (steepTeaBag) can be abstracted to a function called “brew”. </li></ul><ul><li>The last step can be abstracted to a function “addCondiments” </li></ul>05/27/10
    39. 39. Template <ul><li>Pg 283 </li></ul><ul><li>The class CaffeineBeverage contains the function prepareRecipe that contains the four steps. </li></ul><ul><ul><li>Note that it is marked as final. </li></ul></ul><ul><ul><li>The steps brew and addCondiments have been marked as abstract. </li></ul></ul><ul><li>The sub-classes will specify the exact implementation of brew and addCondiments. </li></ul>05/27/10
    40. 40. Template Method Pattern <ul><li>GoF Intent: “The Template Method pattern defines the skeleton of an algorithm in a method, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps of the algorithm without changing the algorithm’s structure .” </li></ul>05/27/10
    41. 41. Hooks <ul><li>“ A hook is a method that is declared in the abstract class, but only given an empty or default implementation.” </li></ul><ul><li>Hook may be optional! </li></ul><ul><ul><li>Empty Implementation </li></ul></ul><ul><li>See pg 292 for an example. </li></ul><ul><li>The hook could be completely empty as well! </li></ul>05/27/10
    42. 42. Hollywood Principle <ul><li>“ Don’t call us, we’ll call you.” </li></ul><ul><li>Promotes working with abstractions as much as possible. </li></ul><ul><li>Good for creating frameworks. </li></ul>05/27/10

    ×