Intent
There are common situations when classes differ only in their behavior. For this cases is a good idea to isolate the algorithms in separate classes in order to have the ability to select different algorithms at runtime.
Strategy Pattern defines a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
Let’s use a real life example to define the problem and see how to use Strategy Pattern can be applied to solve it
Real-life Example
Suppose you are trying to design a game, to be specific, it is about swiming racing. Here are different swimming strokes that we will support in the game:
Now, let’s think how we could implement each swimming techniques in the programming point of view.
Naively, the first solution or method in my mind is that could think about is using Inheritance
and override each method in the child class. Here is an UML example of how the class should be inherited.
For example: in BreastStrokeSwimming
class, we will override the each method in the parent class (Swimming
). Then let’s assume that some of the swimming styles share the same implementation logic, for example, maybe warmUp()
and coolDown()
are exactly the same across all styles but not for BackStrokeSwimming
, legMove()
method is the same for FreeStyleSwimming
and BackStrokeSwimming
. Given these assumption, we will noticed that code blocks for method warmUp()
, coolDown()
and legMove()
will be duplicated to some extents.
public class Swimming {
private Time endurance;
public void warmUp() {}
public void coolDown() {}
public void breathe() {}
public void armMove() {}
public void legMove() {}
}
public class FreeStyleSwimming extends Swimming {
private Time endurance;
@Override
public void warmUp() {
// ...
}
@Override
public void coolDown() {
// ...
}
@Override
public void breathe() {}
@Override
public void armMove() {}
@Override
public void legMove() {}
}
As a developer, we should always align with Don’t repeat yourself principlie when writing codes. But what is the solution?
Actually this is when Strategy Pattern could come in to save out time. Consider the following UML for the changes we make:
- Parent class still contains those methods and child classes override each method
- However, child classes take dependency on a new Interface variable, for example:
IBreathStrategy
and there will be two classesSimpleBreatheStrategy
andAdvancedBreatheStrategy
which implement the interface forbreathe()
method.- note: in the UML, I just add the implement class for interface
IBreathStrategy
for simplification, actually every interface will have specific strategy class to implement the actual logic
- note: in the UML, I just add the implement class for interface
What benefits does this approach give?
- Reduce Code Redundancy
- Since actual logic of
breathe()
ormove()
method is abstracted benhind the interface, in the child class, we can simply dobreatheStrategy.breathe()
in the method. - In this way, one logic could be used everywhere applicable, without duplicating the whole code block
- Since actual logic of
- Single Responsibility
XXXStrategy
Class is just for the logic/implementation of one single method, which the implementation is abstracted fromXXXSwimming
instances we instantiated
Problem
Figure demonstrates how this is routinely achieved - encapsulate interface details in a base class, and bury implementation details in derived classes. Clients can then couple themselves to an interface, and not have to experience the upheaval associated with change: no impact when the number of derived classes changes, and no impact when the implementation of a derived class changes.
A generic value of the software community for years has been, “maximize cohesion and minimize coupling”. The object-oriented design approach shown in figure is all about minimizing coupling. Since the client is coupled only to an abstraction (i.e. a useful fiction), and not a particular realization of that abstraction, the client could be said to be practicing “abstract coupling” . an object-oriented variant of the more generic exhortation “minimize coupling”.
A more popular characterization of this “abstract coupling” principle is “Program to an interface, not an implementation”.
Clients should prefer the “additional level of indirection” that an interface (or an abstract base class) affords. The interface captures the abstraction (i.e. the “useful fiction”) the client wants to exercise, and the implementations of that interface are effectively hidden.
Pros and Cons
Pros | Cons |
---|---|
Each strategy (algorithm/implementation) is easily inter-changable | Overkill if the strategy never changes |
Each strategy’s implementation is abstracted from the main class | Could confuse clients on which is the proper strategy to use |
Favor composition instead of inheritance | Complicated for programming language which support functional programming |
Open/Close Principle | |
(Optional) SIngleton Pattern could be applied to create instances for each strategy |