PHP Strategy Design Pattern: Learn Using Code Examples in 2023

Last Updated on

CraftyTechie is reader-supported. When you buy through links on our site, we may earn an affiliate commission.

What is the Strategy Pattern in PHP?

Strategy patterns in PHP can be trickier initially. We have done our best to convey the idea using an example of an application called Robotron. Here’s what the architecture of this simulation game looks like after applying the strategy design pattern.

Strategy Pattern in PHP
Robotron strategy design pattern

Feeling overwhelmed? Well, it happens when you are unsure what the pattern is. But we hope that by the end of this article, you’ll be in a better position to understand this seemingly complicated design.

Definition | Strategy Design Pattern in PHP

Strategy design pattern in PHP is a structural design pattern that defines a set of algorithms and encapsulates them in separate modules such that their objects are interchangeable.”

Strategy Pattern in PHP
Strategy design pattern

The objective of this pattern is to encapsulate the variable strategies so that the client code can refer to it and alter it in the runtime. 

Sounds complicated? Let’s build a simulation game, Robotron and apply the pattern incrementally to let you experience when and why you should use the strategy design pattern in PHP.

Robotron | A Real World Example of Strategy Pattern in PHP

Robotron simulation game | Version 1.0

A company has planned a simulation game that includes variants of robots. These robots have different characteristics and behaviours. The initial version of the game includes two robots – RED & BLUE.

Red & blue robots
Red & blue robots

Developers find it easy and develop a comparatively straightforward OO design. They develop a superclass Robot with appropriate methods and let the sub-classes RoboRed & RoboBlue inherit those behaviours. This way, they try to avoid code duplication and promote flexible design so new robots class will inherit from the Robot class.

Strategy Pattern in PHP
Robotron v1.0

The RoboRed & RoboBlue classes override the display() and talk() methods to customise it – because they look different and say different dialogue. The walk() method is common, and the sub-classes don’t need to override it.

Here comes the flying robot…

The following day, designers added a robot that could walk and fly. They name it Robo purple. It has these cool robotic wings.

purple robot
Purple robot

The developers are hasty, but they are not to blame them because the deadline is by the end of the day. The product team has planned to run the simulation the next day. So, the developers add a fly() method to the Robot class. They let the new RoboPurple class inherit it. The design looks as follows now.

Strategy Pattern in PHP

The developers could barely have time to test the simulation and assumes it would turn up well in the product review. But something unexpected, somewhat funny, happens. All the robots can fly 😂 You can see what happened there, right? All the classes inherit the fly() method because they inherit from the Robot class.

We need a quick fix!

So, the developers come up with something quick, overriding the fly() method in RoboRed and RoboBlue to do nothing. Here’s what it looks like afterwards.

robotron UML

So, the RoboRed and RoboBlue don’t fly anymore. That does work out for now until RoboGreen shows up.

RoboGreen | The silent robo…

So, we have yet another robot. This time it is RoboGreen who can’t talk and fly. It can only walk.

Strategy Pattern in PHP

Now developers will override the talk() and fly() methods in the new RoboGreen sub-class to ensure it behaves as expected. Following is the new OO design now.

UML robotron

See! Every robot is different, and developers need to override methods. That not only increases code duplication and defeats the purpose of usability that the design was meant to promote.

Meanwhile, the product team releases the plan for the coming six months. They want a total of five hundred different robots in the simulation game. Now, the developers are reflecting on the current design, and it seems it won’t do well in the longer run. Think of overriding different subsets of methods 496 different sub-classes. 

So, developers are thinking hard, and they come up with another design.

Robotron simulation game | Version 2.0

Developers think it will be wise to extract the changing behaviours from the Robot class into their interfaces. For instance – Talkable and Flyable. This way, every robot class will implement relevant interfaces. For example – only the RoboPurple will implement the Flyable interface. The rest won’t bother because they don’t have to fly.

Now, the new design looks like this.

Strategy Pattern in PHP

Though it looks sophisticated, it solves the problem: Only some robots talk or fly. However, if we think more about flexibility and maintenance – it certainly has some flaws. 

So, What’s Wrong with this Implementation?

Code Duplication & Change

The present design lets the sub-classes implement the relevant interfaces. So, if a robot can fly, its sub-class will implement the fly() method from the interface Flyable

So, Developers need to do code duplication in all those sub-classes which implement the fly method.

Suppose 350 out of 500 robots can fly. Now, developers will do code duplication in these 350 classes. The same goes for the other interface Talkable. Doesn’t that defeat the whole purpose? Developers wanted to avoid duplication and are now back to square zero.

But that’s not all because the good old friend has yet to come – CHANGE.

One change is enough for a maintenance nightmare!

What if there’s a requirement to change the flying algorithm? Developers could do nothing except revisit those 350 classes and change the fly() method in each of them. That’s one heck of a job.

But wait for another quirk! What if there are different flying algorithms? Suppose we have three distinct flying modes – slow, normal and fast. That’s three different algorithms. Now, developers have to take extra precautions and make changes to each of these algorithms in 350 different sub-classes.

That’s a maintenance nightmare. You see! One change and the flaws in the design are out screaming. 

What if your application grows to one thousand robots? Developers have to think hard and develop a design to meet the flexibility and maintainability requirements of the application. Now, it is time for a more focused effort. 

Encapsulate the Change

“Isolate the changing aspects of your application in their own modules”. That encapsulates the change and forms the basis of almost every design pattern.

How is it helpful? When you contain the changing aspects of your application in a separate module, it has the least side effects on the constant elements of your application. So it is easier to introduce changes and extend your application without causing the entire application to adapt.

So, what are the changing aspects of Robotron? 

The behaviours are the changing aspects, right? So, what the developers have to do is to encapsulate the behaviours and their concrete implementations. 

encapsulation

The changing behaviours…

The behaviours are encapsulated such that there’s an interface with concrete implementations extending it.

Strategy Pattern in PHP

What are the benefits of this design?

  • Developers can reuse the concrete implementations in robot sub-classes.
  • Any changes to these behaviours are made in these classes than the sub-classes.
  • Developers can add more behaviours to it easily.

That’s a clever way to encapsulate the changing behaviours and contain the change, and that’s one chunk of the strategy pattern. 

The next piece of the puzzle is connecting these behaviours to the robots. 

Has-a Relationship vs Inheritance

Developers have been using inheritance, also called “is-a” relationships. For instance – a red robot is-a robot so is the purple one. A sub-class is-a more specialized type of parent class. 

But there’s another possible relationship between classes and their objects known as composition or “has-a” relationship. It happens when an object keeps a reference to another. 

Let’s say object A has a reference to object B. Object A is said to have a “has-a” relationship with object B. In other words, object A is composed of object B. For example – A robot has a head, eyes, antennas, and feet. That’s what the robot has been composed of. But a robot also “has-a” set of behaviours. 

That’s what changes the most. Different robots with different sets of behaviour. So, the Robot class need to refer to these behaviour objects. 

The Abstract Robot Class

The Robot class has to be transformed now. It now refers to these behaviour classes as follows.

robot class
Robot class
Strategy Pattern in PHP

The Robot class delegates fly and talk behaviours to the behaviour objects. The sub-classes will assign the type of behaviours on the fly as follows.

robogreen pseudo

Observe that the RoboGreen class initialize NoFly and NoTalk objects to reflect its behaviour. That’s a powerful design pattern there. But wait, there is more. We can dynamically change behaviours using getters and setters.

Strategy Pattern in PHP

Complete Architecture | Strategy Pattern in PHP

Strategy Pattern in PHP

Strategy Design Pattern in PHP

The following code recreates the strategy design pattern in PHP for the Robotron simulation game.

Code | Strategy Design Pattern in PHP

<?php
 
abstract class FlyBehaviours {
 
    abstract public function fly();
}
 
class FlySlow extends FlyBehaviours {
    public function fly() {
        echo "I fly slow";
    }
}
 
class FlyNormal extends FlyBehaviours {
    public function fly() {
        echo "I fly normal";
    }
}
 
class FlyFast extends FlyBehaviours {
    public function fly() {
        echo "I fly fast".PHP_EOL;
    }
}
 
class NoFly extends FlyBehaviours {
    public function fly() {
        echo "I don't fly".PHP_EOL;
    }
}
 
abstract class TalkBehaviours {
 
    abstract public function talk($dialouge);
}
 
class Talk extends TalkBehaviours {
    public function talk($dialouge) {
        echo $dialouge;
    }
}
 
class NoTalk extends TalkBehaviours {
    public function talk($dialouge) {
        echo "I don't talk".PHP_EOL;
    }
}
 
abstract class Robot {
    private $flyBehaviour;
    private $talkBehaviour;
    public $dialouge = "";
 
    public function performFly() {
        $this->flyBehaviour->fly();
    }
 
    public function performTalk() {
        $this->talkBehaviour->talk($this->dialouge);
    }
 
    public function setFlyBehaviour($fb) {
        $this->flyBehaviour = $fb;
    }
 
   
    public function setTalkBehaviour($tb) {
        $this->talkBehaviour = $tb;
    }
 
    abstract public function display();
 
    public function walk() {
        echo "I'm walking".PHP_EOL;
    }
 
}
 
class RoboRed extends Robot {
 
    function __construct($dialouge) {
        $this->dialouge = $dialouge;
        $this->setFlyBehaviour(new NoFly());
        $this->setTalkBehaviour(new Talk($dialouge));
    }
 
    public function display() {
        echo "Rendering RoboRed".PHP_EOL;
    }
}
 
class RoboBlue extends Robot {
 
    function __construct($dialouge) {
        $this->dialouge = $dialouge;
        $this->setFlyBehaviour(new NoFly());
        $this->setTalkBehaviour(new Talk($dialouge));
    }
 
    public function display() {
        echo "Rendering RoboBlue".PHP_EOL;
    }
}
 
 
class RoboPurple extends Robot {
 
    function __construct($dialouge) {
        $this->dialouge = $dialouge;
        $this->setFlyBehaviour(new FlyFast());
        $this->setTalkBehaviour(new Talk($dialouge));
    }
 
    public function display() {
        echo "Rendering RoboPurple".PHP_EOL;
    }
}
 
 
class RoboGreen extends Robot {
 
    function __construct($dialouge) {
        $this->dialouge = $dialouge;
        $this->setFlyBehaviour(new NoFly());
        $this->setTalkBehaviour(new NoTalk($dialouge));
    }
 
    public function display() {
        echo "Rendering RoboGreen".PHP_EOL;
    }
}
 
function main() {
    $roboPurple = new RoboPurple("I'm RoboPurple\n");
    $roboPurple->display();
    $roboPurple->performTalk();
    $roboPurple->walk();
    $roboPurple->performFly();
 
    $roboGreen = new RoboGreen("");
    $roboGreen->display();
    $roboGreen->performTalk();
    $roboGreen->walk();
    $roboGreen->performFly();
}
 
main();
?>

Output | Strategy Pattern in PHP

Rendering RoboPurple
I'm RoboPurple
I'm walking
I fly fast
Rendering RoboGreen
I don't talk
I'm walking
I don't fly

Pros and Cons of the Strategy Pattern in PHP

ProsCons
Satisfies the Open/Closed principle. You can extend behaviours without changing the existing code.It can be overkill if the algorithms don’t change at all.
Uses strong “has-a” relationship between objects.Modern programming languages use anonymous functions with typed functions. So you won’t need extra interfaces and classes to implement this pattern.
Encapsulates the variable algorithms in separate modules. A client has to be mindful of the differences in the algorithms to use the correct one.
The algorithms are swappable at runtime.

Where is Strategy Design Pattern Used?

  • When similar classes use a different set of behaviours, then strategy patterns could be helpful. This pattern encapsulates these behaviours in separate modules to support flexibility and maintainability.
  • When classes shuffle algorithms dynamically. Strategy Pattern lets you change the algorithms in the object at runtime.
  • When you want to separate concerns such that there’s a separate module for client code and the variable algorithms.
  • When you want to avoid code duplication in the client code. Without the strategy pattern, you may write code for similar algorithms, often across different client sub-classes.

Frequently Asked Questions

What type of design pattern is strategy?

A strategy design pattern is a behavioural design pattern that deals with algorithms and how they are assigned to the objects.

What are the strategy design pattern characteristics?

Strategy design pattern encapsulates the set of variable algorithms in a separate module. The module is open to extension but close to modification, satisfying the open/closed principles. 

Also, these algorithms are interchangeable as they implement the same parent class. You can dynamically change these algorithms in the client code using polymorphism and composition.

When to consider the use of the strategy design pattern?

When you have similar classes that have different sub-set of behaviours. Rather than hardcoding these behaviours in every class, you can encapsulate them in a separate module and integrate them with the right classes using composition. When the classes have to perform these behaviours, they delegate it to these algorithm objects.

Using the Strategy Pattern in PHP Today

Phew! That was alot of content. Let’s summarise what we have seen thus far. The strategy pattern is a behavioural design pattern encapsulates the different implementations in separate modules with a common interface. Thereby, these implementations are interchangeable because of polymorphism.

The article uses an example of a Robotron application, a simulation game for rendering different types of robots with varying algorithms. The initial design kept all the concrete implementation in the Robot class, which led to duplication in the sub-classes. Following this design, the developers develop an interface segregation approach for the robots’ behaviours.

This approach had limitations and challenges that badly affected code maintainability and flexibility. The final OO design uses a strategy pattern to encapsulate various implementations in a separate module. The Robot class established a composition relationship with these behaviour objects and delegated the behaviour calls to these objects.

So, we started from a non-maintainable, inflexible design to a more flexible and maintainable one.  That’s all because of the clever strategy.
Hope you have been able to take away some value. Stay tuned for more at FuelingPHP.

Books on Design Patterns

Want to learn more about Design Patterns? There are many great resources online. We recommend the following books for your collection as they both can teach you the theoretical and the application of using design patterns in your day-to-day programming. Feel free to use the following Amazon affiliate links if you’d like to purchase them and a way to support our efforts.

Design Patterns: Elements of Reusable Object-Oriented Software

Design Patterns: Elements of Reusable Object-Oriented Software book

This is the book that started it all. I believe that every programmer should have a referenced copy to this book at some point in their career. There have been many updates and excellent newer content through the years, but this is a classic. It still stands the test of time and is just as relevant for today as it was in the original printing in the 90s.

Check it out on amazon

Learning PHP Design Patterns

This is an excellent book to go beyond the theory and apply it to writing good PHP code. Learning PHP Design Patterns is published by the popular O’Reilly media company. O’Reilly consistently publishes some of the most useful reference material related to software development. They are known to provide materials that thoroughly cover a topic in a way that is simple to understand. I recommend this book to every PHP developer.

Check it out on amazon

Want to see our full review of books on design patterns? Read our huge review article on over 15 design pattern books.

Design Patterns in PHP Learning Series.

This article is part of our series of design patterns in PHP. We are going through all of the patterns and showing how they can help you build better applications. Browse through our full list of patterns below.

Did you find this article helpful?

Join the best weekly newsletter where I deliver content on building better web applications. I curate the best tips, strategies, news & resources to help you develop highly-scalable and results-driven applications.

Build Better Web Apps

I hope you're enjoying this article.

Get the best content on building better web apps delivered to you.