The strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. It allows an algorithm's behavior to vary independently from clients that use it. The strategy pattern can help manage complex conditional logic by replacing conditionals with object composition. Walkthrough examples demonstrate extracting an algorithm into strategy objects from a class to vary behaviors independently through composition rather than inheritance.
3. What is it?
“Define a family of algorithms, encapsulate each one, and
make them interchangeable. Strategy lets the algorithm
vary independently from clients that use it.” Gang of Four
5. Why would I use it?
Strategy lets the algorithm vary independently from clients that use it
Strategy pattern allows you to prefer composition over inheritance
Adhere’s to the Open/Closed principle of SOLID
Helps manage the complexity of having numerous variations of an
algorithm
8. Walkthrough
Conditional logic in a variant controls which of several
variants of a calculation are executed.
Create a strategy for each variant and make the method
delegate the calculation to a Strategy instance.
12. Delegate to your Concrete
Strategy
public double capital() {
return new CapitalStrategy().capital(this);
}
public double capital() {
return new CapitalStrategy().capital(expiry, maturity, commitment, duration(), riskFactor()...);
}
Pass the context as parameter
Pass the necessary data parameters
13. Pass the context as parameter
- Making context methods visible to obtain
information, breaking ‘information hiding’
+ When your class gets new public methods they’re
instantly available
14. Pass the necessary data
parameters
- All data will be passed to all strategies regardless if
it needs that data
+ Involves the least coupling between the context and
the strategy
16. Pass the context as
parameterpublic class Loan {
….
public double capital() {
return new CapitalStrategy().capital(this);
}
double duration() {
return 0d;
}
int getUnusedPercentage() {
return new Random().nextInt(1);
}
double outstandingRiskAmount() {
return 0d;
}
double unusedRiskAmount() {
return 0d;
}
Date getExpiry() {
return expiry;
}
Date getMaturity() {
return maturity;
}
double getCommitment() {
return commitment;
}
}
17. Move functionality related to capital calc
public class CapitalStrategy {
public double capital(Loan loan) {
if (loan.getExpiry() == null && loan.getMaturity() != null) {
return loan.getCommitment() * duration() * riskFactorFor(loan);
}
if (loan.getExpiry() != null && loan.getMaturity() == null) {
if (getUnusedPercentage() != 1) {
return loan.getCommitment() * getUnusedPercentage()
* duration() * riskFactorFor(loan);
} else {
return (outstandingRiskAmount()
* duration() * riskFactorFor(loan))
+ unusedRiskAmount()
* duration() * unusedRiskFactorFor(loan);
}
}
return 0d;
}
...
18. Extract Field
public class Loan {
private final Date expiry;
private final Date maturity;
private final double commitment;
private final CapitalStrategy strategy;
public Loan(Date expiry, Date maturity, double commitment) {
this.expiry = expiry;
this.maturity = maturity;
this.commitment = commitment;
this.strategy = new CapitalStrategy();
}
public double capital() {
return strategy.capital(this);
}
...
...
Date getExpiry() {
return expiry;
}
Date getMaturity() {
return maturity;
}
double getCommitment() {
return commitment;
}
}
19. Extract Parameter
public static Loan newTermLoad(Date expiry, Date maturity, double commitment) {
return new Loan(expiry, maturity, commitment, new CapitalStrategy());
}
public static Loan newRevolver(Date expiry, Date maturity, double commitment) {
return new Loan(expiry, maturity, commitment, new CapitalStrategy());
}
public Loan(Date expiry, Date maturity, double commitment, CapitalStrategy capitalStrategy) {
this.expiry = expiry;
this.maturity = maturity;
this.commitment = commitment;
this.strategy = capitalStrategy;
}
20. Replace conditional with Polymorphism
public class CapitalStrategyTermLoan extends CapitalStrategy {
public double capital(Loan loan) {
return loan.getCommitment() * duration() * riskFactorFor(loan);
}
@Override
protected double duration() {
return 0d; // specific calculation for term loans
}
}
public static Loan newTermLoad(Date expiry, Date maturity, double commitment) {
return new Loan(expiry, maturity, commitment, new CapitalStrategyTermLoan());
}
21. Replace conditional with Polymorphism
public static Loan newTermLoad(Date expiry, Date maturity, double commitment) {
return new Loan(expiry, maturity, commitment, new CapitalStrategyTermLoan());
}
public static Loan newRevolver(Date expiry, Date maturity, double commitment) {
return new Loan(expiry, maturity, commitment, new CapitalStrategyRevolver());
}
public static Loan newAdvisedLine(Date expiry, Date maturity, double commitment, int riskRating) {
if(riskRating > 3) return null;
return new Loan(expiry, maturity, commitment, new CapitalStrategyAdvisedLine());
}
24. Benefits
+ Clarifies algorithms by decreasing or moving conditional
logic
+ Simplifies a class by moving variations of an algorithm
to a hierarchy
+ Enables one algorithm to be swapped for another at
runtime
25. Liabilities
- Complicates a design when simpler refactorings like
compose method are available
- Complicates how an algorithm obtains or receives data
from their context class
29. Refactoring ‘towards’ a
pattern- Refactor to, towards or away
- Some patterns are all or nothing
- Aim is always for a better design
- Evaluate if the design has improved
- Pools of Insight
http://www.industriallogic.com/papers/khdraft.pdf
30. Further Reading
Refactoring - Martin Fowler
Refactoring to Patterns - Joshua Kerievsky
Head First Design Patterns - Elisabeth Freeman
Design Patterns - Gof
https://www.goodreads.com/genres/novoda-new-starts-
reading
This is my strategy. There are many many like it, but this one is mine.
the strategy pattern (also known as the policy pattern)
The strategy pattern is the creation an inter-changeable family of algorithms interchangeable at run-time.
You can think of the strategy pattern in use at a Formula One race track, the tyres are inter-changeable at runtime, they can be changed when the car comes into the pit lane depending on how the car is running, track conditions, weather, other drivers. It’s always a car but the tyre style can change.
Using composition allows you to have a Car that has a normal brake strategy or a break with ABS strategy.
Allowing the car to be open to extension but closed to modification. (Modification & inheritance would mean someone can subclass & override braking behavour).
Enough TALK, let’s get hands on and run through a real scenario for using the Strategy Pattern.
You can introduce parameter objects, or pass data through constructors
Passing the context is much simpler at the moment because otherwise i’d have to pass 8+ parameters
Passing the context is much simpler at the moment because otherwise i’d have to pass 8+ parameters
Passing the context is much simpler at the moment because otherwise i’d have to pass 8+ parameters
Passing the context is much simpler at the moment because otherwise i’d have to pass 8+ parameters
Passing the context is much simpler at the moment because otherwise i’d have to pass 8+ parameters
Passing the context is much simpler at the moment because otherwise i’d have to pass 8+ parameters
Passing the context is much simpler at the moment because otherwise i’d have to pass 8+ parameters
Passing the context is much simpler at the moment because otherwise i’d have to pass 8+ parameters
Once size does not fit all
When not to use replace conditional with strategy pattern
When to consider replace conditional with polymorphism
Decorator & Strategy both eliminate conditional logic around special cases or alternative behaviour.
If the code under inspection is part of an inheritance hierarchy each algorithm might map to one subclass.
if not already inheritance you have to consider if your algorithm just depends on one type code, you might be better creating subclasses.
No type code - go for strategy.
Swapping algorithms at runtime, you should avoid inheritance based approaches because this would mean changing the type of the object the client is working with rather than substituting a strategy.