Strategy Design Pattern

This tutorial looks at the Strategy Design Pattern which is defined as – “groups of encapsulated algorithms that can be used interchangeably”. It is one of the design patterns popularized in the Design Patterns book by ‘Gang of Four’. Strategy Design pattern is a behavioral pattern. We will look at various aspects of the Strategy pattern with an example. The example that we look at is a car manufacturing algorithm.

Example of Strategy Design Pattern

We build a class that can design a car. Given a make of the car, the class configures the car with an engine, body configuration, manufacturer etc. The ‘algorithm’ is the logic to build the car

Here’s our car class, we have omitted the getters and setters for sake of brevity.

package com.studytrails.patterns.java.strategy;

public class Car
{

	private String manufacturer;
	private String engineType;
	private String enginePlacement;
	private String bodyConfiguration;
	private String make;

	
	public String getEngineType()
	{
		return engineType;
	}

	public void setEngineType(String engineType)
	{
		this.engineType = engineType;
	}

	// Other getters and setters omitted.	

}

Lets see how you would build a car in a non design pattern world. We build two cars

package com.studytrails.patterns.java.strategy;

public class CarBuilder
{
	public static void main(String[] args)
	{
		CarBuilder carBuilder = new CarBuilder();
		carBuilder.buildCar("mazda6-sport");
		carBuilder.buildCar("ferrari-california");
	}

	private Car buildCar(String make)
	{
		Car car = new Car(make);
		if ("mazda6-sport".equals(make)) {
			car.setBodyConfiguration("sedan");
			car.setEnginePlacement("front");
			car.setEngineType("SkyActiv-G");
			car.setManufacturer("Mazda");
		} else if ("ferrari-california".equals(make)) {
			car.setBodyConfiguration("convertible");
			car.setEnginePlacement("front-engine, rear wheel drive");
			car.setEngineType("V8");
			car.setManufacturer("Ferrari");
		}
		return car;
	}
}

We build two types of cars in this example – a ‘mazda6-sport’ and a ‘ferrari-california’. The algorithm to build the car is inside the
buildCar
method. Depending on the make the relavant if loop creates the car (by setting the parameters, you didnt expect us to build a real car using software, right?)

Lets now see why our design is not optimal. Here are the reasons

  • The algorithm to build the car is hard coded in the class(context) that uses the algorithm.
  • A single class contains all the algorithms. Any change in one of the algorithms would mean changing the class.
  • If you need a new algorithm this class needs to be modified.

We have realized that our car builder design is not optimum. It would be good if we can remove the algorithms from the Car Builder. In other words, we want to encapsulate what changes. One way is to create an abstract Car Builder and then extend that class by, say, a Mazda6SportCarBuilder and a FerrariCaliforniaCarBuilder. That would work, however, there is a problem. The algorithm is coupled to the Context(CarBuilder). Imagine that we add one more method to the CarBuilder after buildCar – ensureSafety(). There may be 10 different algorithms to build car and only 2 algorithms to ensureSafety. However, with this design we will have to duplicate the ensureSafety method in all at least 5 of the 10 classes

Design Principle – Encapsulate what changes

Our design aim is to modify the design so that with any changes in requirements the changes to the class are minimal. We can do so in two ways. The first way is to add new behaviour by extending behaviour from an existing class (by extending that class). The other way is to separate the part that changes from the part that does not change and then inject the parts that can change. Either way, there is an important design principle :

Open-Close Design Principle – Open for Extension, closed for modification.

Strategy pattern uses the second option. In our example, it would be good if the algorithm to build the cars could be separated from the main CarBuilder class and then the appropriate algorithm can be injected at runtime. The CarBuilder (context) class does not need to know what implementation it gets (it does not need to know whether it gets a Mazda builder or a Ferrari builder ) because it does not hold the reference to a concrete object, rather it holds a reference to an interface that is implemented by both the Mazda or Ferrari builder. The advantage is that if you plan to build a new car , say a Merc model, then all that you need to do is implement the same interface and you are done. There is another important design principle here, and probably a very important one too:

Code to an Interface – Prefer coding to an interface, i.e., storing variables as a reference to an interface rather to a concrete class.

This applies to everyday coding too. For example, for most of the collections it would be preferable to store the reference to the Collection interface rather than the concrete implementation, so create an ArrayList but store the variable as a List

	List<String> carNames = new ArrayList<String
	

Definition of Strategy Design Pattern :

The strategy design pattern defines a group of encapsulated algorithms that can be used interchangeably. The context (client) that uses the algorithm contains the reference to the interface and not the implementation and hence the implementation can be changed without modifying the client.

Let us now modify the CarBuilder so that it implements the Strategy Design Pattern. Here’s the modified class diagram

Car Builder Strategy Pattern

So we have removed the logic to build the car into the MazdaBuilderStrategy and FerrariBuilderStrategy classes. Both these classes implement the CarBuilderStrategy interface. The client (CarBuilderClient) has a reference to the CarBuilderStrategy interface. We pass in the appropriate strategy to the client. Here’s the complete working example.

public class CarBuilderClient
{

	private CarBuilderStrategy strategy;

	public static void main(String[] args)
	{
		CarBuilderClient client = new CarBuilderClient();
		client.setStategy(new MazdaBuilderStrategy());
		Car mazda = client.buildCar("mazda6-sport");
		client.setStategy(new FerrariBuilderStrategy());
		Car ferrari = client.buildCar("ferrari-california");

	}

	public void setStategy(CarBuilderStrategy stategy)
	{
		this.strategy = stategy;
	}

	public Car buildCar(String make)
	{
		return strategy.buildCar(make);
	}
}

The strategy interface

public interface CarBuilderStrategy
{
	public Car buildCar(String make);
}

The strategy implementations

public class FerrariBuilderStrategy implements CarBuilderStrategy
{

	@Override
	public Car buildCar(String make)
	{
		Car car = new Car(make);
		car.setBodyConfiguration("convertible");
		car.setEnginePlacement("front-engine, rear wheel drive");
		car.setEngineType("V8");
		car.setManufacturer("Ferrari");
		return car;
	}
}

public class MazdaBuilderStrategy implements CarBuilderStrategy
{

	@Override
	public Car buildCar(String make)
	{
		Car car = new Car(make);
		car.setBodyConfiguration("sedan");
		car.setEnginePlacement("front");
		car.setEngineType("SkyActiv-G");
		car.setManufacturer("Mazda");
		return car;
	}

}

Before we finish the tutorial, there is one more thing that you should know about the strategy pattern. There are cases where information needs to be passed from the client to the algorithm. For example, in our example, we might need to pass the color of the car to the Strategy. There are two ways to pass information to the client

  • Pass the reference of the client to the strategy. so in our example we can create a setter for the client in the strategy implementation class.
    	public void setClient(CarBuilderClient client){
    	   this.client = client.
    	}
    	and then use the properties from this client (Color is a property of the client)
    	
  • The other way is to pass only the property from the client to the strategy.
    	public void setColor(String color){
    	  this.color=color;
        }
    	

This completes our tutorial on the Strategy pattern. Please feel free to leave your comments if you have any questions. We would appreciate if you could share this page if you find it useful.

Leave a Comment