In this article, we will briefly look at the strategy pattern, its benefits and how to apply it to system design.

The strategy pattern is classed as a behaviour pattern; it allows the behaviours of a class to be changed at run time.

To illustrate how to apply the strategy pattern, we’ll start with a simple class diagram which describes relationships between objects in a make-believe system.

Vehicle – an abstract class which details the properties and operations that child classes inherit from it. The abstract class cannot be instantiated directly, only via a child class.

Car, Van, Bus – these are concrete classes that inherit properties and operations from the abstract vehicle class.

If we look at the ‘makeEngineNoise’ operation, this operation makes a particular noise, and the chosen noise depends on the type of fuel, the no of cylinders and the engine size. For example, a 4 cylinder 2-litre engine would sound completely different to a 4.5 litre, 8 cylinder petrol engine. To make this work, the operation would contain a number of if statements to determine the noise that needs to be made (it could get complicated if there are lots of variations).

Let us say that you have been asked to design a game, the design above allows different vehicles to make different sounds depending on their properties. It works, and you pass the design off to the developers to build. A year later you are asked to design some new functionality into the game, players will be able to swap out engines in their vehicles in order to improve performance, no problem you think, we will just add an operation to the Vehicle to allow those properties to be changed, so you create something like this and add it to the Vehicle class.

public void changeEngine(Double engineSize, Integer noOfCylinders, String fuelType) {
        this.engineSize = engineSize;
        this.noOfCylinders = noOfCylinders;
        this.fuelType = fuelType;
    }

Great, you have solved the problem, but wait, you have also been asked to implement electric engines too, the solution you have come up with is not very flexible is it?

We can come up with a better solution, but first, we need to take what changes in our Vehicle objects and encapsulate them. When we want to change an engine in a vehicle, the properties engineSize, noOfCylinders and fuelType all change, so we can remove them from the Vehicle class and give them their own class. We’ll also remove the makeEngineNoise() operation.

The above design allows us to use composition and pass an instance of an Engine into a Vehicle when it is instantiated, while also allowing us when needed to easily replace a vehicle’s engine at run-time using the new changeEngine() operation on the Vehicle. We have created an abstract class Engine which holds properties and operations that may be common between different types of engines, and then we have created concrete classes for the different types of engines, and each will be responsible for generating the sound for that particular type of engine. In future, should we need to change the way a petrol engine sounds, we only need to make a change to the PetrolEngine class and there is no risk it will cause issues with electric or petrol engine sounds (Single Responsibility Principle).

The code below shows how we might implement the above design, create a new Car and change its Engine.

Vehicle myCar = new Car(
    "Ford", /* Manufacturer */
    "Focus", /* Model */
    4, /* No of passengers */
    new PetrolEngine(
        1.6, /* Engine size */
    	4 /* No of cylinders */
    	)
    );

/* Ask the engine to make a noise */
myCar.getEngine().makeEngineNoise();

/* Swap the vehicles engine */
myCar.changeEngine(new DieselEngine(3.0, 6));

/* Ask the engine to make a noise, this time it will be different as it's a different engine */
myCar.getEngine().makeEngineNoise();

When we create a new vehicle we pass in an instance of the engine via the constructor, this is the engine that the vehicle will use initially (1.6 litres, 4 cylinder petrol engine). Later we use the changeEngine() operation to give the Car a new engine (3.0 litre, 6 cylinder diesel). In this design the Vehicle knows it needs an Engine, but it does not care what type of engine it is, the relationship is loosely coupled, and as a result of this we could create any number of different types of engines (electric, hybrid, veggie oil, fuel cell) and assign any of them to a Vehicle as all engine behaviour belongs to the Engine itself and not the vehicle.

The resulting design is more flexible and future proof, while going some way to making the system more maintainable and testable. Engines themselves can be independently tested to ensure their behaviours are correct.


Leave a Reply

Your email address will not be published. Required fields are marked *

Signup to the Newsletter

Get useful tips on web application development and software engineering.