strategy and template pattern

Post on 13-Dec-2014

3.491 Views

Category:

Technology

1 Downloads

Preview:

Click to see full reader

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

04/10/23 1

Design Patterns

Week 2: Strategy and Template

Jonathan Simonjonathan_simon@yahoo.com

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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:

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

Question 4a

public void setLogWriter(ILogWriter writer) {

this.logWriter = writer;

}

2404/10/23

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…

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

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

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

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

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?

Refactoring (cont)

public interface IComputeStrategy {

int Compute();

}

public class DefaultComputeStrategy implements IComputeStrategy {

public int Compute() {

return (SOME_CONST*50)+100;

}

}

3104/10/23

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

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

Injection References

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

cc163739.aspx

3404/10/23

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

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

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

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

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

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

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

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

top related