1. Overview

In this tutorial, we're going to investigate the Strategy Pattern and see how we can implement it in Java.

2. When to Implement

Assume that we have an algorithm. It can be sorting a list of numbers or outputting the log statements. There are multiple ways of sorting numbers. Similarly, there are multiple ways to output log statements.

If we aren't careful, we can bake these algorithms into the client code directly. Consequently, we lose the chance of changing the algorithm at runtime - like switching to a variant.

Moreover, if an algorithm needs to be used by multiple clients, each client writes the same code causing code duplication. As a result, the algorithm code will be hard to update and maintain.

We can avoid these problems by defining classes that encapsulate different algorithms. An algorithm encapsulated in this way is called a strategy.

3. How to Implement

We must first have an interface representing an algorithm. Then we must provide multiple implementations to serve as different strategies.

We'll work with the duck abstraction. A duck can fly and quack. Thus we have two interfaces corresponding to these behaviors: Flyable and Quackable. And we'll provide different implementations for these interfaces.

Let's start with the Flyable interface:

public interface Flyable {

    void fly();
}

Flyable has a single method, fly. It also has two implementations.

public class FlyWithWings implements Flyable {

    public void fly() {
        System.out.println("I fly with my wings!");
    }
}
public class FlyNoWay implements Flyable {

    public void fly() {
        System.out.println("I cannot fly!");
    }
}

 

Then we have the Quackable interface:

public interface Quackable {

    void quack();
}

We also defined two implementations for it.

public class Squeak implements Quackable {

    public void quack() {
        System.out.println("Squeak!");
    }
}
public class MuteQuack implements Quackable {

    public void quack() {
        System.out.println("Sshhh!");
    }
}

At this moment, we have two algorithms - flying and quacking algorithms. And there are two different versions for each algorithm.

The client code - in this case the Duck class - can make use of these algorithms through composition.

public class Duck {

    private Flyable flyBehavior;
    private Quackable quackBehavior;

    public Duck() {
        flyBehavior = new FlyNoWay();
        quackBehavior = new MuteQuack();
    }

    public void setFlyBehavior(Flyable flyBehavior) {
        this.flyBehavior = flyBehavior;
    }

    public void setQuackBehavior(Quackable quackBehavior) {
        this.quackBehavior = quackBehavior;
    }

    public void performFly() {
        flyBehavior.fly();
    }

    public void performQuack() {
        quackBehavior.quack();
    }
}

Here, the Duck class holds references to both Flyable and Quackable interfaces. Moreover, these references can change at runtime through setter methods:

public class MainClient {

    public static void main(String[] args) {
        Duck duck = new Duck();
        duck.performQuack();
        duck.performFly();

        duck.setFlyBehavior(new FlyWithWings());
        duck.setQuackBehavior(new Squeak());
        duck.performQuack();
        duck.performFly();
    }
}

4. Summary

In this tutorial, we've investigated the Strategy Pattern and its implementations in Java.

Finally, check out the source code for all examples over on Github.