Design Patterns: Strategy Pattern

 

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 classes SimpleBreatheStrategy and AdvancedBreatheStrategy which implement the interface for breathe() 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

What benefits does this approach give?

  1. Reduce Code Redundancy
    1. Since actual logic of breathe() or move() method is abstracted benhind the interface, in the child class, we can simply do breatheStrategy.breathe() in the method.
    2. In this way, one logic could be used everywhere applicable, without duplicating the whole code block
  2. Single Responsibility
    1. XXXStrategy Class is just for the logic/implementation of one single method, which the implementation is abstracted from XXXSwimming 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  

Reference