Strategy Pattern

Imagine that you need to create a game where a character is able to fight. Character should be able to pick a weapon. Different weapon would preform different action. We need the character to be able to use his fists, a knife, and a gun. Every weapon can have its own set of attributes and actions. It makes sense to encapsulate every weapon in a class.

When we run the game, we must assign weapon to our character. Maybe initially the character can only have fists to fight with. We can assign him fists. But now you might start to notice that it wouldn’t be possible to assign different kind of weapon, because it is now expects object of type Fists only. If we pass type Knife or Gun, we would get an error.

Even if we get over that fact, you don’t have to look far ahead to notice that game can have more than 3 weapons. And creating extra weapons would be actually quite time consuming. What if there are more than one developer making a game? How would they go about creating a weapon if they get such a task? What if they make their own, completely different from yours, implementation of weapons, with their own sets of methods. Well, that would result in a very painful and long development process, which is quite pointless.

If you know a little bit about something named “interface”, you know that writing an interface is like creating a contract: every class that will “implement” this interface will be bound by the terms of this contract to fulfill its minimum requirements, namely, implement a particular set of methods. What this means in our case is if we want to avoid a huge mess of different implementations, we should make sure all of weapon classes implement our Weapon interface.

Now as we have done that, several things happened:

  1. All of the weapon classes have same set of minimum methods
  2. All of the weapon classes have a super type of Weapon
  3. It is very easy from now on to create new types of weapons

Why is it important to have the same super type for all of the weapon classes? Remember, we have tried to assign a particular weapon to our character. If we want to change that that would not be possible since our assignment function will be expecting a particular type of a weapon. And if we made it possible for it to only accept Fists type, it wouldn’t know how to deal with other, and raise an exception. With the same super type across all of weapons what we have done is allowed any object that is of type Weapon to be accepted as a parameter. That means that if initially we assign Fists object to our character, at some point during execution of the code, it would be very easy to assign a different weapon, like Knife or Gun, because they are also Weapon type. This is what enables us to select different algorithm at a runtime. And that’s a Strategy Pattern.


Strategy Pattern:

defines a family of algorithms, encapsulates each one, and makes them interchangeable. Lets the algorithm vary independently from clients that use it.


Here we also encounter several other OOP concepts and principles.

When we write a class for character, we do not try to write a lot of subclasses for characters with different weapon. Instead, we define a Weapon object in Character class, and thus Character delegates its weapon behavior instead of defining it on its own. That’s delegation.

When we put Weapon objects in Character class like that, it significantly reduces the complexity that would have otherwise being created if we were to use Inheritance. That’s an example of Composition. And we should always favor it over inheritance.

We have identified that all of the weapon objects share similar structure. Their behavior differs. We have identified the aspects of our game that vary and separated them from what stays the same.

Lastly, when we decided to use the power of interface, we have abstracted the weapon behavior, and made it more dynamic. That is a very desired outcome, and that’s why we should as often as possible (and reasonable) program to an interface instead of implementation. Below you can see a couple of examples.

Another example of Strategy Pattern in diagram
https://commons.wikimedia.org/wiki/File:Strategy.png
// Billing Strategy Example in Java
// Source: https://en.wikipedia.org/wiki/Strategy_pattern
import java.util.ArrayList;

@FunctionalInterface
interface BillingStrategy {
    // use a price in cents to avoid floating point round-off error
    int getActPrice(int rawPrice);
  
    //Normal billing strategy (unchanged price)
    static BillingStrategy normalStrategy() {
        return rawPrice -> rawPrice;
    }
  
    //Strategy for Happy hour (50% discount)
    static BillingStrategy happyHourStrategy() {
        return rawPrice -> rawPrice / 2;
    }
}

class Customer {
    private final ArrayList<Integer> drinks = new ArrayList<>();
    private BillingStrategy strategy;

    public Customer(BillingStrategy strategy) {
        this.strategy = strategy;
    }

    public void add(int price, int quantity) {
        this.drinks.add(this.strategy.getActPrice(price*quantity));
    }

    // Payment of bill
    public void printBill() {
        int sum = this.drinks.stream().mapToInt(v -> v).sum();
        System.out.println("Total due: " + sum / 100.0);
        this.drinks.clear();
    }

    // Set Strategy
    public void setStrategy(BillingStrategy strategy) {
        this.strategy = strategy;
    }
}

public class StrategyPattern {
    public static void main(String[] arguments) {
        // Prepare strategies
        BillingStrategy normalStrategy    = BillingStrategy.normalStrategy();
        BillingStrategy happyHourStrategy = BillingStrategy.happyHourStrategy();

        Customer firstCustomer = new Customer(normalStrategy);

        // Normal billing
        firstCustomer.add(100, 1);

        // Start Happy Hour
        firstCustomer.setStrategy(happyHourStrategy);
        firstCustomer.add(100, 2);

        // New Customer
        Customer secondCustomer = new Customer(happyHourStrategy);
        secondCustomer.add(80, 1);
        // The Customer pays
        firstCustomer.printBill();

        // End Happy Hour
        secondCustomer.setStrategy(normalStrategy);
        secondCustomer.add(130, 2);
        secondCustomer.add(250, 1);
        secondCustomer.printBill();
    }
}

And that’s the Strategy Pattern. In my opinion, one of the most useful design patterns. Once you see it for what it is, you will start to recognize where you can apply it. Its application cases have very similar traits across the board.

One thought on “Strategy Pattern

Leave a comment