strategy and template pattern

42
06/07/22 1 Design Patterns Week 2: Strategy and Template Jonathan Simon [email protected]

Upload: jonathan-simon

Post on 13-Dec-2014

3.491 views

Category:

Technology


1 download

DESCRIPTION

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."

TRANSCRIPT

Page 1: Strategy and Template Pattern

04/10/23 1

Design Patterns

Week 2: Strategy and Template

Jonathan [email protected]

Page 2: Strategy and Template Pattern

Agenda Strategy Pattern

SimUDuck Application (Head First) Setting Behavior Dynamically Favor Composition over Inheritance Lab Refactoring w/Strategy Dependency Injection

Template Pattern Hooks Hollywood Principle

204/10/23

Page 3: Strategy and Template Pattern

SimUDuck Application (Head First Design Patterns)

pg 2 in book.

Duck can Quack Swim Display – handles visual display

It is assumed that all Ducks have the same behavior for Quack and Swim.

304/10/23

Page 4: Strategy and Template Pattern

SimUDuck Application

pg 3

Now fly() behavior is added to the superclass. This is a new requirement! Problem since RubberDucks don’t fly!

pg 4-5. How can this problem be solved? We could override fly() in RubberDuck to do nothing. Also note that RubberDucks do not quack..they Squeak.

What was good for reuse is bad for maintenance. Note: a change to the superclass unintentionally can affect all

subclasses. You would have to inspect each subclass.404/10/23

Page 5: Strategy and Template Pattern

Using Interface

pg 6 – we learn that the superclass flying and quacking behavior will constantly change (due to changing customer requirements) What would need to happen every time the behavior changed?

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. How will each Duck sublcass define those behaviors? Could there be possible duplication of code? Ie, MallardDuck’s

implementation of fly() versus RedheadDuck’s implementation. What if there are different variations of flying?

504/10/23

Page 6: Strategy and Template Pattern

The problem… Inheritance didn’t work

Not all subclasses need to Fly or Quack. Making a change to superclass will cause a maintenance

headache.

Flyable/Quackable interface didn’t work No code re-use among the different Fly/Quack behaviors. If Fly changed, you would need to track down all subclasses that

use the Flyable interface.

Design Principle: “Identify the aspects of your application that vary and separate them from what stays the same.” You can alter or extend parts that vary without affecting the parts

that are static. 604/10/23

Page 7: Strategy and Template Pattern

Back to SimUDuck

Which part varies? What part does not?

How can we pull out the behavior that varies from the Duck class?

Also, how can we extend this behavior in the future without having to re-write existing code?

704/10/23

Page 8: Strategy and Template Pattern

Closer Look at Variable Behavior pg 13: Create two sets of classes (one for Flying and

one for Quacking). These two sets of classes represents the behavior that varies.

We’d like the behaviors to be flexible, for example: 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.”

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??

804/10/23

Page 9: Strategy and Template Pattern

Design Principle #2

“Program to an interface, not an implementation.” (I like “program to an abstraction, not an implementation.”)

Define the interfaces FlyBehavior and QuackBehavior. The Duck class will be tied to these interfaces, but not a specific implementation.

Each Duck subclass will use a specific implementation of this behavior.

904/10/23

Page 10: Strategy and Template Pattern

Breaking it down…

pg 13. FlyBehavior and QuackBehavior The FlyBehavior and QuackBehavior represents an abstraction

of the flying and quacking. FlyWithWings, FlyNoWay, Quack, etc are implementations of

these behaviors.

pg 15. The Duck class contains instance variables of FlyBehavior and QuackBehavior. Here is an example of programming to an abstraction. The Duck

class (an abstraction itself) is composed of the abstraction of two behaviors.

Pg 15. performQuack function() The operation of Quacking is delegated! The Duck class does

not do it itself. 1004/10/23

Page 11: Strategy and Template Pattern

Breaking it down… (cont)

Pg 16. In the constructor of MallardDuck, the quack and fly behavior is set up. 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.

However, this can be changed dynamically.

pg 18 Testing the Duck Code

1104/10/23

Page 12: Strategy and Template Pattern

Setting Behavior Dynamically

pg 20.

1. Add two new methods to Duck class

2. ModelDuck is created with some default behavior.

3. Create a new fly behavior

4. Note that ModelDuck is created..then the behavior is changed.

1204/10/23

Page 13: Strategy and Template Pattern

To Think About

If we had used inheritance for Fly(), could you change the Fly behavior at runtime?

Look at the FlyBehavior classes…what if there is common functionality amongst all of the FlyBehavior classes? Instead of an interface, use an abstract class for FlyBehavior. Think of this as a “family of algorithms”

1304/10/23

Page 14: Strategy and Template Pattern

Favor Composition over Inheritance

Pg 23

With Composition, we were able to dynamically change behavior at runtime.

With Inheritance, behavior was decided a compile time. You could not change at runtime when you use inheritance.

1404/10/23

Page 15: Strategy and Template Pattern

The Strategy Pattern

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.”

1504/10/23

Page 16: Strategy and Template Pattern

Labpublic class Logger {

public void LogMessage(string message, int level) {

PrintSteam[] files = OpenFiles();

WriteLogMessage(files, message, int level);

CloseFiles(intfiles);

}

private void WriteLogMessage(PrintStream[] files,

string message, int level) {

for (int i=0; i < files.Length; i++) {

if (level == 1) {

//Write error message to log file.

else if (level == 2)

//Write error message to log file + Log the

//UserName and IP address.

else if (level == 3)

//Write error message to log file. + Send email to

//administrator

}

}16

Page 17: Strategy and Template Pattern

Lab (cont)Question 1: Which part of the code varies?

Question 2: What part of the code does not vary?

Question 3: Using inheritance, describe how you could isolate the parts that vary.

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?

1704/10/23

Page 18: Strategy and Template Pattern

Lab (cont)

Question 4: Using composition, design out how you would isolate the parts that vary.

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.

Question 4a: How can we change the logging level during runtime?

1804/10/23

Page 19: Strategy and Template Pattern

Lab (cont)

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.

Logger logger = new Logger()

Logger.setLogWriter(new LogWriter1());

Logger.setLogWriter(new LogWriter2());

Is there an alternative to sending an instance of LogWriter1 or LogWriter2, and yet achieve the same results?

1904/10/23

Page 20: Strategy and Template Pattern

Lab (cont)

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?

Bonus Question: For a Logger implementation (such as LogWriter1), how many instances do we really need?

2004/10/23

Page 21: Strategy and Template Pattern

Lab Answers

Question 1 - In WriteLogMessage function, the if/else statement with the three different options.

Question 2: The three lines of code in function LogMessage The for loop declaration in function WriteLogMessage

2104/10/23

Page 22: Strategy and Template Pattern

Lab Answers

Question 3

public abstract class Logger {

public void LogMessage(string message) {…}

private void WriteLogMessage(PrintStream[] files, string message) {

for (int i=0; i < files.Length; i++) {

WriteLogMessageImpl(PrintStream file, message);

}

}

protected abstract WriteLogMessageImpl(PrintStream file, string msg);

}

2204/10/23

Question 3a:

Page 23: Strategy and Template Pattern

Lab Answers

Question 4

public interface ILogWriter {

void Write(PrintStream file, string message);

}

public class Logger {

public ILogWriter logWriter;

private void WriteLogMessage(PrintStream[] files, string message) {

for (int i=0; i < files.Length; i++) {

logWriter.Write(file, message);

}

}

}

2304/10/23

Page 24: Strategy and Template Pattern

Question 4a

public void setLogWriter(ILogWriter writer) {

this.logWriter = writer;

}

2404/10/23

Page 25: Strategy and Template Pattern

Question 4bpublic enum LogWriterEnum {

Simple,

Medium,

Hard

}

public void setLogWriter(LogWriterEnum logEnum) {

if (logEnum == Simple)

this.logWriter = LogWriter1();

else if (logEnum == Medium)

this.logWriter = LogWriter2();

}

Alternative Design:

http://www.codeproject.com/KB/architecture/FactoryStrategyDesignPatt.aspx

2504/10/23

So what if we created an enum…

Page 26: Strategy and Template Pattern

Question 4c

Option 1

public interface ILogWriter {

void Write(PrintStream file, string message,

string LoggedInUser, string LastTimeLogged);

}

We can send the information in the method call itself. The disadvantage is that we may be sending more information than needed.

2604/10/23

Page 27: Strategy and Template Pattern

Question 4c

Option 2 – We can pass a reference of Context (Logger) itself

public interface ILogWriter {

void Write(PrintStream file, string message, Logger log);

}

Advantage - The strategy can ask the Logger class for information that it needs. (and it may not need anything!)

Disadvantage – The strategy becomes more tightly coupled with the Context object.

2704/10/23

Page 28: Strategy and Template Pattern

Bonus Question

We can use Singletons!

Instead of this code:

Logger.setLogWriter(new LogWriter1());

Logger.setLogWriter(new LogWriter2());

We can use this code:

Logger logger = new Logger()

Logger.setLogWriter(LogWriter1.TheInstance);

Logger.setLogWriter(LogWriter2.TheInstance);

2804/10/23

Page 29: Strategy and Template Pattern

Advantage: Unit Testing

What if a new behavior is needed? Ie, new FlyBehavior, new QuackBehavior, new LogWriter, etc

The new class can be created and unit tested on its own (in complete isolation of the infrastructure that uses it).

Another developer who does not know the “Big Picture” can easily be given direction to create the new behavior and unit test it.

2904/10/23

Page 30: Strategy and Template Pattern

Refactoring w/Strategy

public class SomeClass() {

public void Function1() {

//section of code that does not vary

//The next line of code will vary

int i = (SOME_CONST*50)+100;

//section of code that does not vary

}

}

3004/10/23

First question you need to ask yourself: use inheritance or composition?

Page 31: Strategy and Template Pattern

Refactoring (cont)

public interface IComputeStrategy {

int Compute();

}

public class DefaultComputeStrategy implements IComputeStrategy {

public int Compute() {

return (SOME_CONST*50)+100;

}

}

3104/10/23

Page 32: Strategy and Template Pattern

Refactoring (cont)

public class SomeClass {

private IComputeStrategy strategy;

public SomeClass() {

this.strategy = new DefaultComputeStrategy();

}

public void Function1() {

//section of code that does not vary

int i = strategy.Compute();

//section of code that does not vary

}

}

3204/10/23

Page 33: Strategy and Template Pattern

Dependency InjectionTechnique for providing an external dependency to a software

component.

In the Refactoring example, SomeClass is dependent upon a concrete implementation:

public SomeClass() {

this.strategy = new DefaultComputeStrategy();

}

But what if we did this… public SomeClass(IComputerStrategy strategy) { this.strategy = strategy;

}

3304/10/23

Page 34: Strategy and Template Pattern

Injection References

http://martinfowler.com/articles/injection.html http://msdn.microsoft.com/en-us/magazine/

cc163739.aspx

3404/10/23

Page 35: Strategy and Template Pattern

Summary

Pattern Name – Strategy Problem – Different algorithms will be appropriate at

different times. Need a way for an object to use different algorithms at runtime.

Solution Define a family of classes that use the same interface Provide a set method so that the behavior can be set (and

changed) at runtime.

Consequences Hierarchy of strategy classes Alternative to subclassing Eliminates conditional statements Clients must be aware of strategies Communication between Context and Strategy

3504/10/23

Page 36: Strategy and Template Pattern

Template

pg 276 contains the requirement on brewing coffee and tea.

Look at code on pg 277 (Coffee) and pg 278 (Tea)

How many different steps are there?

What is similar?

What is different?

3604/10/23

Page 37: Strategy and Template Pattern

Template

Pg 280. Coffee’s implementation of prepareRecipe() would have

to call the following: boilWater brewCoffeeGrinds pourInCup addSugarAndMilk

Tea’s implementation of prepareRecipe() would have to call the following: boilWater steepTeaBug pourInCup addLemon

3704/10/23

Page 38: Strategy and Template Pattern

Template

Pg 282

The second step of Coffee (brewCoffeeGrinds) and the second step of Tea (steepTeaBag) can be abstracted to a function called “brew”.

The last step can be abstracted to a function “addCondiments”

3804/10/23

Page 39: Strategy and Template Pattern

Template

Pg 283

The class CaffeineBeverage contains the function prepareRecipe that contains the four steps. Note that it is marked as final. The steps brew and addCondiments have been marked as

abstract.

The sub-classes will specify the exact implementation of brew and addCondiments.

3904/10/23

Page 40: Strategy and Template Pattern

Template Method Pattern

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.”

4004/10/23

Page 41: Strategy and Template Pattern

Hooks

“A hook is a method that is declared in the abstract class, but only given an empty or default implementation.”

Hook may be optional! Empty Implementation

See pg 292 for an example.

The hook could be completely empty as well!

4104/10/23

Page 42: Strategy and Template Pattern

Hollywood Principle

“Don’t call us, we’ll call you.”

Promotes working with abstractions as much as possible.

Good for creating frameworks.

4204/10/23