Strategy Pattern

Strategy Pattern
Like the Abstract Factory but for
methods
Our first pattern that deals with methods rather than classes.
What’s 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. That’s “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 shouldn’t 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);
}
}
//here’s 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,
it’s 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();
}
}
//doesn’t 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. It’s much better
cohesion than having the
DataManager decide!
public class SortInventory
{
public void sort(Data data)
{
SortStrategy strategy = new ShellSort();
//I’ll invoke a “tester” class
DataTester tester = new DataTester(data);
if(tester.almostSorted())
{
strategy = new InsertionSort();
}
}
}
strategy.sortAlgorithm(data);
But it’s 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 it’s 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();
//I’ll 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.
It’s your policy. Could also use info passed in
from a “client” via the ModemDriver and
dialPhone() method.
But there’s 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 you’ll need the
Observer Pattern. That’s coming up next!
Ok, Your Turn…
Teams of 2.
Come up with a UML example that needs the
strategy pattern.
Fix the other team’s UML.
Now go back to your original example and
write the code for it (using the fixed UML).