Strategy in PHP [Design pattern with examples]
Coding (Php 7.x)
The amazing way to use composition over inheritance
If you want to skill up your knowledge of programming (not only in PHP but in general) learning design patterns is incredibly useful.
You can think of design patterns as reliable and long-lasting solutions to well-known problems that web developers around the world have encountered before you.
There are a few of those and each of them has a catchy name that describes, or at least tries to describe what they do,
Today we are going to look at the Strategy
The Strategy pattern that we are going to see in this article is one if not, the simplest design pattern you can learn.
Strategy lets you clarify that coding (when done right) It’s about composition and shows you the limitation of inheritance (and the fact that inheritance is not meant for code reuse).
So what is the Strategy pattern?
Strategy defines a family of algorithms, it encapsulates each one of them and makes them interchangeable.
Strategy lets the algorithm vary independently from clients that use it.
Let’s break this definition down!
Strategy defines a FAMILY OF ALGORITHMS when you use the strategy pattern you have a family of algorithms.
it ENCAPSULATES EACH ONE of them and makes them interchangeable. this means that you have several algorithms and each of them can be used interchangeably
Strategy lets the algorithm VARY INDEPENDENTLY from clients that use it. We decouple the algorithm from the one who is using it (the client), it makes the algorithms independent, we do not have to change the algorithm and the client at the same time.
Confused?
No worries, we’ll break down all the step below.
The series
This article is part of the series “Design Pattern in PHP [with examples]”
There are already several episodes available, you can check them just below.
- Factory Method Pattern
- Decorator Design Pattern
- Observer Design Pattern
- Repository Pattern
- Strategy Pattern
An introduction
How to follow this article along?
You have already read the design patterns solve problems,
I believe the best way to learn about is by example so… let’s create a problem.
Let’s say we are hired and need to create a website for a clothing company.
The company sells several types of shirts and each of them has different features.
To better understand design pattern we show them graphically, using UMLs
A basic UML would look like the one shown below.
We got a parent class (Shirt) and a bunch of children classes,
We should include long-sleeve shirts, Hawaiian shirts, formal shirts, polo shirts and so on.
All the classes we have seen so far are shirts, this means that all the children inherit from the parent class.
What problem is the Strategy pattern is solving
When our code includes inheritance we find that all the classes have the same methods,
Probably the parent class will have just the definition of the method whereas the concrete ones got their own implementation.
For our example let’s say that all these classes should have a getPrice(), a inStock() and a getPercentageSale() method.
At first impression, the application should work just fine until you realise that each of the shirts must have its own implementation.
Maybe the Hawaiian shirts are on sale for 50% or the polo for 25% what if by business choice both polo and formal shirts need to be on sale for the same percentage all the time.
What can you do then,
Well, as a first solution that came into my mind we can think that if the formal shirt and the polo shirt need to be on sale at the same time for the same amount maybe we can create another class that wraps this behaviour and that is then extender by both these types.
But what then the polo comes from the same warehouse as the long-sleeve one.
Then what? Should we create another class and extends this feature too?
I guess you are even more confused right now so let me explain,
What we are experiencing is a pretty common issue with hierarchy and programming.
You see,
hierarchy works ok until we keep it vertically, we have a few children shirt classes that inherit specific behaviours from a parent Shirt class.
What inherits fails to deliver is reliability and scalability when we talk about horizontal inheritance.
In this case, each child has its own implementations.
When some of them share a few implementations with others in the same level of hierarchy (between siblings to put is simply).
But more often than not there are no rules on how to implement them.
The solution
To begin with, let me note that design patterns are polyglot, you can use them in every language as long as you are presented with the issue you want to solve.
In this example Nam Nguyen currently a software engineer at Axon shows us how to quickly implement the strategy pattern using Golang.
Below instead we continue with our shirt management example using PHP.
so, what is all this about?
What very smart programmers realized is that in cases like this, it is way better to EXTRAPOLATE THE BEHAVIOURS (the features) of the objects rather than the objects themselves.
The best way to do that is by creating interfaces that are only responsible for that specific behaviour.
Let’s see how!
Implementing the behaviours
We know that there are different types of shirts that can be sold under the same type of sale, this is the family of algorithms the official definition was talking about.
there is a group of algorithms that are all about offers and discount percentage, hence they are a family.
How do we translate this in code?
Introducing PercentageSaleStrategyInterface.
As you may have understood this is not a class but rather an interface and what it does is to provide the getPercentageSale() method to the classes that implement it.
interface PercentageSaleStrategyInterface { public function getPercentageSale(): int; }
I am sure you know how an interface works but if you don't have a look at “php interfaces explained” and its detailed explanation by Dayle Rees.
In short, you cannot implement an interface, or better you cannot invoke methods with no code,
To solve this we need at least a class that implement getPercentageSale() and actually returns some results back to us.
class SummerSaleStrategy implements PercentageSaleStrategyInterface { public function getPercentageSale(): int{ return 15; } } class BlackFridaySaleStrategy implements PercentageSaleStrategyInterface { public function getPercentageSale(): int{ return 50; } } class BoxingDaySaleStrategy implements PercentageSaleStrategyInterface { public function getPercentageSale(): int{ return 5; } }
Here you have your family of algorithms.
now we only need to make them interchangeable.
Note that, if you think the snippet of code above is too complicated you might want to step back a bit and have a look at the basics of PHP.
Here we have our updated UML,
Now our Shirt Class “has a” percentage sale strategy that is an interface (something that defines a contract) and then we have SummerSaleStrategy that “is an” object of the same type of strategy alongside their brothers BlackFridaySaleStrategy and BoxingDaySaleStrategy.
A thought that I had the first time I saw this and you might have right now is why we have complicated the code so much and created these other classes and relationships.
The reason is that we now have multiple strategies (or behaviours) that do not conflict with each other because of their vertical relationship and that can be used to compose the type of Shirt that we want.
Plus you can add these behaviours for multiple features on objects of the same class (do the shirts have buttons or need cufflink? What if they are short-sleeved?) you can have as many combinations as you want now.
You now only need to COMPOSE the object
Composition over inheritance
Let’s sum up the situation of our classes so far,
The core of our code at the moment is our Shirt Class, we have several classes then that inherit from it,
Eventually, we have created the PercentageSaleStrategyInterface since it is an interface, it cannot be implemented so we have added some children class SummerSaleStrategy, BlackFridaySaleStrategy and BlackFridaySaleStrategy.
If you look at the first UML right now you might realize that we do not actually need the children of the Shirt Class anymore.
If fact, the Strategy pattern is a promoter of using the composition coding style.
If you want to understand what composition is in detail you can look it up on my blog or have a look at this brilliant post by Tyler McGinnis exactly about Inheritance vs Composition
Thanks to dependency injection we can now inject all the strategy we need and create an object from the Shirt class itself.
We just transformed our code from a very dependent and low scalable one to something that uses a particular configuration of an instance of strategies (or behaviours).
So once, during the creation of a shirt object, you give a combination of all these strategies you can create a different Shirt every time, at runtime.
Of course, as we just said this is only possible by the use of dependency inject, so let’s see quickly some code, and how to do this in real life
class Shirt { private $sleeve; private $collar; private $sale; public function __construct( SleeveStrategyInterface $sleeve, CollarStrategyInterface $collar, PercentageSaleStrategyInterface $sale ) { $this->sleeve = $sleeve; $this->collar = $collar; $this->sale = $sale; } }
public function getPercentageSale() {
return $this->sale->getPercentageSale();
}
}
As you can see from the code the construct needs 3 parameters, they are type-hinted as the interface instead of the concrete class.
this is a very important rule,
Always code to an interface rather than a concrete class.
You will see why in a second.
These values are saved as parameters inside our class that then take reference of then and uses then when the given method is called.
$summerShirt = new Shirt(new ShortSleeveStrategy, new WingTipCollarStrategy, new SummerSaleStrategy); $shirt->getPercentageSale();
Here is the instantiation of a shirt, in here we are passing the concrete strategies, that are part of the interface we have given before.
In this case, we can change the type of strategy whenever we want.
(That’s the power of coding to interfaces)
Conclusion
Here is another piece of the design pattern collection gone.
The practice of programming implies the implementation of lots of problem in our code and luckily for us, several of these problems have already been figured out.
We are here today to study how to include these design pattern in our code.
As web developers, a rule of thumb is that the lazy we are the better we become in coding.
There is no reason to reinvent the wheel.
in fact,
if we encounter a problem it is very likely that someone in the world has solved it already or has a better solution than us (this is why website such as StackOverflow and packagist.org thrive).
What to do next?
What you have seen so far is a fair advanced php topic, if for any reason anything was not clear and you want to step back a bit you can brush up a bit of Object-oriented programming.
If that does not satisfy your learning hunger and you prefer to step out of PHP for a bit I got you,
Here is article the first article of the series Introduction to Composer.