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

The strategy pattern is classed as a behaviour pattern, it allows 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 will inherit from it. The abstract class cannot be instantiated directly, only via a child class

Car, Van, Bus - these are concrete classes will inherit properties and operations from the abstract vehicle class

If we look at the ‘makeEngineNoise’ operation, this operation will make a particular noise, and the chosen noise will depend 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’s say you’ve 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’re 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, let’s 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’ve solved the problem, but wait, you’ve also been asked to implement electric engines too, the solution you’ve come up with isn’t 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 let’s 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’s instantiated, while also allowing us when needed to easily replace a vehicles engine at run-time using the new changeEngine() operation on the Vehicle. We’ve created an abstract class Engine which holds properties and operations that may be common between different types of engines, and then we’ve 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’s 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 engine via the constructor, this is the engine that the vehicle will use initially (1.6 litre, 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 doesn’t 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.

Comments

There are no comments, why not be the first?

Submit a reply

Your e-mail address will not be published, all fields are required.