Strategy pattern in Java and Python Development 22.02.2017

Behavioral Design Patterns provide solution for the better interaction between objects and how to provide lose coupling and flexibility to extend easily. Following design patterns come under this category.

Strategy pattern allows you to switch the algorithm or strategy based upon the situation.

The Strategy pattern is useful when there is a set of related algorithms and a client object needs to be able to dynamically pick and choose an algorithm from this set that suits its current need. The Strategy pattern suggests keeping the implementation of each of the algorithms in a separate class. Each such algorithm encapsulated in a separate class is referred to as a strategy. An object that uses a strategy object is often referred to as a context object.

In other words, Strategy pattern is used when we have multiple algorithms for a specific task and client decides the actual implementation to be used at runtime. Strategy pattern is also known as Policy pattern. We define multiple algorithms and let client application pass the algorithm to be used as a parameter. One of the best example of this pattern is Collections.sort() method that takes Comparator parameter. Based on the different implementations of Comparator interfaces, the objects are getting sorted in different ways.

The Strategy pattern is one way that composition can be used as an alternative to subclassing. Rather than providing different behaviors via subclasses overriding methods in super classes, the strategy pattern allows different behaviors to be placed in concrete strategy classes which share the common strategy interface. A context object contains a reference to a strategy. By changing the context's strategy, different behaviors can be obtained.

dp_strategy.gif

There are three participants in the Strategy pattern

  • Strategy defines an interface common to all supported algorithms. Context uses this interface to call the algorithm defined by a ConcreteStrategy.
  • ConcreteStrategy. Each concrete strategy implements an algorithm.
  • Context contains a reference to a strategy object. The context objects contains a reference to the ConcreteStrategy that should be used. When an operation is required then the algorithm is run from the strategy object. The context is not aware of the strategy implementation.

Let's take a look at how some client might put the Strategy pattern into action.

dp_strategy_seq.png

When to use the Strategy design pattern

  • Many related classes differ only in their behavior. Strategies provide a way to configure a class with one of many behaviors.
  • You need different variants of an algorithm. For example, you might define algorithms reflecting different space/time trade-offs. Strategies can be used when these variants are implemented as a class hierarchy of algorithms.
  • An algorithm uses data that clients shouldn’t know about. Use the Strategy pattern to avoid exposing complex, algorithm-specific data structures.
  • A class defines many behaviors, and these appear as multiple conditional statements in its operations. Instead of many conditionals, move related conditional branches into their own strategy class.

Java

Here is an example of the strategy pattern. First, we'll define a Strategy interface. It declares a checkTemperature() method.

File Strategy.java.

public interface Strategy {
    boolean checkTemperature(int temperatureInF);
}

The HikeStrategy class is a concrete strategy class that implements the Strategy interface. The checkTemperature method is implemented so that if the temperature is between 50 and 90, it returns true. Otherwise it returns false.

File HikeStrategy.java.

public class HikeStrategy implements Strategy {
    @Override
    public boolean checkTemperature(int temperatureInF) {
        if ((temperatureInF >= 50) && (temperatureInF <= 90)) {
            return true;
        } else {
            return false;
        }
    }
}

The SkiStrategy implements the Strategy interface. If the temperature is 32 or less, the checkTemperature method returns true. Otherwise it returns false.

File SkiStrategy.java.

public class SkiStrategy implements Strategy {
    @Override
    public boolean checkTemperature(int temperatureInF) {
        if (temperatureInF <= 32) {
            return true;
        } else {
            return false;
        }
    }
}

The Context class contains a temperature and a reference to a Strategy. The Strategy can be changed, resulting in different behavior that operates on the same data in the Context. The result of this can be obtained from the Context via the getResult() method.

File Context.java.

public class Context {
    int temperatureInF;
    Strategy strategy;

    public Context(int temperatureInF, Strategy strategy) {
        this.temperatureInF = temperatureInF;
        this.strategy = strategy;
    }

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

    public int getTemperatureInF() {
        return temperatureInF;
    }

    public boolean getResult() {
        return strategy.checkTemperature(temperatureInF);
    }
}

The Demo class creates a Context object with a temperature of 60 and with a SkiStrategy. It displays the temperature from the context and whether that temperature is OK for skiing. After that, it sets the Strategy in the Context to HikeStrategy. It then displays the temperature from the context and whether that temperature is OK for hiking.

File Demo.java.

public class Demo {
    public static void main(String[] args) {
        int temperatureInF = 60;

        Strategy skiStrategy = new SkiStrategy();
        Context context = new Context(temperatureInF, skiStrategy);

        System.out.println("Is the temperature (" + context.getTemperatureInF() + "F) good for skiing? " + context.getResult());

        Strategy hikeStrategy = new HikeStrategy();
        context.setStrategy(hikeStrategy);

        System.out.println("Is the temperature (" + context.getTemperatureInF() + "F) good for hiking? " + context.getResult());
    }
}

Python 3

import abc

class Strategy(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def check_temperature(self, temperature):
        pass


class HikeStrategy(Strategy):
    def check_temperature(self, temperature):
        if temperature >= 50 and temperature <= 90:
            return True
        else:
            return False


class SkiStrategy(Strategy):
    def check_temperature(self, temperature):
        if temperature <= 32:
            return True
        else:
            return False

class Context:
    def __init__(self, temperature, strategy):
        self.temperature = temperature
        self.strategy = strategy

    def set_strategy(self, strategy):
        self.strategy = strategy

    def get_temperature(self):
        return self.temperature

    def get_result(self):
        return self.strategy.check_temperature(temperature)


if __name__ == '__main__':
    temperature = 60

    strategy_ski = SkiStrategy()
    context = Context(temperature, strategy_ski)

    print("Is the temperature ({} F) good for skiing? {}".format(context.get_temperature(), context.get_result()))

    strategy_hike = HikeStrategy()
    context.set_strategy(strategy_hike)

    print("Is the temperature ({} F) good for hiking? {}".format(context.get_temperature(), context.get_result()))