Vous êtes sur la page 1sur 23

Strategy Pattern

Like the Abstract Factory but for


methods
Our first pattern that deals with methods rather than classes.

Whats It For?
Sometimes need a different method depending on some
variable.
The methods will all solve the same problem, but in different ways.

A different algorithm depending on circumstances

sometimes use insertion sort algorithm

use when data is almost sorted

sometimes use heapsort algorithm

use when not at all sorted

So the necessary method/algorithm varies.

Business rules can change. Thats business speak for rules


that change depending on some variable. Example: if sales
slumped, then call everythingOnSale() method, else call
gougeEm() method.
So again, the necessary method/algorithm varies.
But methods deal with a similar task setting prices.

Encapsulate this variability!

Bad Code Will Look Like This


public void takeInventory()
{

if(almostSorted)
{
use class1.insertionSort();
}
else if(variable 2)
{
use class2.shellSort()
}
else if()
{

Low cohesion!
(Similar to Abstract Factory)

We will use the


Strategy Pattern
to fix this.

Using a Strategy
A solution to the previous code would be

public void takeInventory(Inventory inventory, SortStrategy strategy)


{

strategy.sort(inventory);
}

This means that the appropriate strategy is aggregated.

The method no longer has to make a series of low cohesion if/else


decisions about sorting.

And can reuse elsewhere.


public void groceryList(SortStrategy strategy)
public void iPodPlayList(SortStrategy strategy)

This can now sort your grocery or play list.

Sorting Example
InventoryManager

SortInventory

+shellSort()
+insertionSort()

Problem: Poor cohesion.


The InventoryManager will have to decide which sort method

to use.
Inventories shouldnt have to decide about sorting algorithms!
Would an inventory manager at your local SuperMart or Burger
Queen be expected to know appropriate sorting strategies from
a data structures class?

In other words, should all the users of your program be expected


to take a data structures class? Does your roommate know data
structures?

Solution: Encapsulate the variable sorting methods.

Inheritance Encapsulation
Solution? (Not Really)
Inheritance often used to (poorly) solve this.
InventoryManager

SortInventory
+sort()

InsertionSort
+sort()

Shellsort
+sort()

Now, each sort can be implemented as necessary.


Problems: (1) Almost encapsulated the variable sort algorithm, but
still deciding with if/else in InventoryManager so not fully encapsulated.
(2) Low cohesion. The inventory still has to know about sorting methods.

Strategy Solution
InventoryManager
-Data data

This encapsulates the


stuff that is variable (i.e.,
the sort technique).
Now SortInventory can
make the if/else
decisions.

if/else
in here
SortInventory
+sort(Data data)

SortStrategy
+sortAlgorithm()

InsertionSortStrategy
+sortAlgorithm()

ShellSortStrategy
+sortAlgorithm()

Strategy Code: Basic Idea


public class InventoryManager
{
pubic void manage()
{
//pick a strategy to use
SortInventory s = new SortInventory(new InsertionSortStrategy());
s.sort(data);
//can change strategies whenever you like
s = new SortInventory(new ShellSortStrategy());
s.sort(data);

//heres another choice! Easy to add new sorting strategy classes.


s = new SortInventory(new HeapSortStrategy());
s.sort(data);

Strategy Code: Basic


public class SortInventory
{
private SortStrategy strategy;

public SortInventory(
SortStrategy strategy)
{
this.strategy = strategy;
}

public void sort(Data data)


{
strategy.sortAlgorithm(data);
}

public class ShellSortStrategy extends


SortStrategy
{
public void sortAlgorithm(Data data)
{
//do your shell sort thing
}
}

See the Flaw?


This is a popular implementation. Heck,
its even in Wikipedia.
But has low cohesion.
Why? Because the InventoryManager is still

deciding which strategy to use.

Instead, push that decision into the

SortInventory class where it makes sense.

Sorting Strategy: Higher


Cohesion
public class InventoryManager
{
private Data data;

pubic void manage()


{
SortInventory s = new SortInventory();

//doesnt have to know anything about sorting techniques.


s.sort(data);

Note high cohesion and encapsulation.


No sorting decisions are made by this class.
All decisions relegated to SortInventory.
All variability in SortInventory and Strategy.

Sorting Strategy: Higher


Cohesion
Nice cohesion! The if/else is in a
class that should know about
sorting methods. Its much better
cohesion than having the
DataManager decide!

public class SortInventory


{
public void sort(Data data)
{
SortStrategy strategy = new ShellSort();

//Ill invoke a tester class


DataTester tester = new DataTester(data);
if(tester.almostSorted())
{
strategy = new InsertionSort();
}

strategy.sortAlgorithm(data);

But its still not ideal. It would be


nice to encapsulate the if/else in a
separate class. See next slide on the
Policy class.

Adding the Policy


Can make a separate class that handles the decision.
A Policy or Configuration class. Not required.
Encapsulates the potential variability in the business rules
InventoryManager

SortInventory

SortingPolicy

+sort(Data data)

-Data data

All the variability


is encapsulated here!

SortStrategy
+sortAlgorithm()

InsertionSort
+sortAlgorithm()

And most of its down


here.

ShellSort
+sortAlgorithm()

if/else
in here.
Now if
customer
or boss
wants
different
Policy,
this is
easy to
replace.

Sort Code

Nice cohesion! The if/else is in the


Policy. If the business policies
change, we can just replace the
Policy class. Modular!

public class SortInventory


{
public void sort(Data data)
{
SortPolicy policy = new SortPolicy(this);
sort(data, policy.getSortStrategy(data));
}
// note aggregation (and overloading)
public void sort(Data data, SortStrategy s)
{
s.sortAlgorithm(data);
}

Handy to have this overloaded method can


use it if have a Client class that lets the
user choose the SortStrategy (for example,
from a drop-down menu).

// This is just like our abstract factory implementation.


// This aggregates the strategies and makes them
// available to the Policy class.
public SortingStrategy getInsertionSortStrategy()
{
return new InsertionSortStrategy();
}

public SortingStrategy getShellSortStrategy()


{
return new ShellSortStrategy();
}

Policy Code

Most variability is encapsulated in this


one Policy class high cohesion. And
it is the only class with an if statement.
Easy to replace this class.

public class SortPolicy


{
private SortInventory sortInventory = null;
public SortPolicy(SortInventory sortInventory)
{
this.sortInventory = sortInventory;
}

pubic SortStrategy getSortStrategy(Data data)


{
SortStrategy strategy = sortInventory.getShellSort();
//Ill invoke a tester class
DataTester tester = new DataTester(data);
if(tester.almostSorted())
{
strategy = sortInventory.getInsertionSort();
}

return strategy;

Note that there is no connection to the


Strategies. We just use the getters from
the SortInventory.

Note: Common trick is to read the correct policy (business rules) from a configuration file.

Modem Example
ModemDriver

DialUpProtocol
+dial()

What can vary? How you dial up! The dial method.
Sometimes need a 1, sometimes need an
extension, sometimes need international prefix, etc.
So how would you solve this?

Modem Strategy Solution


ModemDriver

DialUpProtocol

Policy

+dialPhone()

And add a Configuration/Policy


class if desired.

IntlDialStrategy
+dial()

DialStrategy
+dial()

LocalDialStrategy
+dial()

Modem Strategy Code


public class ModemDriver
{
some method
{
DialUpProtocol dup = new
DialUpProtocol();
dup.dialPhone();
}
}
public class DialUpProtocol
{
public void dialPhone()
{
Policy p = new Policy();
DialStrategy ds = p.getDialStrategy();
ds.dial();
}
}

public class Policy


{
public DialStrategy getDialStrategy()
{
//default strategy
DialStrategy ds = new LocalDialStrategy();
if( )
{
ds = new IntlDialStrategy();
}

return ds;

Can make decision based on anything


you like whether the user ate oatmeal
for breakfast or if the temperature is too hot.
Its your policy. Could also use info passed in
from a client via the ModemDriver and
dialPhone() method.

But theres a connectivity problem! See next slide.

Modem Strategy Problem


Can you see the high connectivity?
Does our Modem code match the UML solution?

No!

High connectivity between the policy and the


DialStrategies.

Can you fix it?

Hint: Try putting getters in the DialUpProtocol. And to use


those getters, you will need to change the constructor of
the Policy.
See the inventory sorting strategy example.
Also see the Abstract Factory example.

Strategy Details
Purpose: Encapsulate related methods that
might change.
i.e., encapsulate a family of related algorithms.
i.e., encapsulate a family of business rules.

Problem (Or When To Use): Use whenever


the choice of algorithm (or rule) will depend
on some variable, or depends on the request
made by the client.

Strategy Details (cont.)


Solution/Implementation: Remove decision about algorithm
from the class (the Client). Put decision in another class (the
Context) that uses the algorithm. The Context class contains an
abstract class (the Strategy) that has an abstract method for
the algorithm. Concrete subclasses will implement the method.
Client

Strategy

Context

+algorithm()

ConcreteStrategy1
+algorithm()

ConcreteStrategy2
+ algorithm()

Strategy Consequences
Higher cohesion.
Greater flexibility/modularity.
Possible class explosion.
Methods must be related.
Each method should solve the same problem

but in a different manner.


If they are not related, then youll need the
Observer Pattern. Thats coming up next!

Ok, Your Turn


Teams of 2.

Come up with a UML example that needs the


strategy pattern.
Fix the other teams UML.
Now go back to your original example and
write the code for it (using the fixed UML).

Vous aimerez peut-être aussi