DECORATOR PATTERN — PHP
In this article, I’ll write about basic decorator pattern. This pattern is really enjoyable because it is like you wrap a present box and another one is in front of your eyes.
If you want to see the other patterns you can visit these below links.
So let’s start with a Team. For example you got a team with some stats and you want to calculate them with some default multipliers.
- Strength : Multiplier = 3
- Agility : Multiplier = 4
- Intelligence : Multiplier = 5
Given stats will be multiplied with these default multipliers. Let’s do it;
There is a team class with setting above parameters for the sake of the example.
class Team
{
public $strength;
public $agility;
public $intelligence;
public function setStrength($stat) {
$this->strength = $stat;
return $this;
}
public function setAgility($stat) {
$this->agility = $stat;
return $this;
}
public function setIntelligence($stat) {
$this->intelligence = $stat;
return $this;
}
}
Let’s set team’s stats;
$team = new Team();
$team->setStrength(7);
$team->setAgility(6);
$team->setIntelligence(5);
Let’s say these classes are all linked to each other and their whole calculation will give us the power of the team.
$teamPower = ($team->strength * 3) + ($team->agility * 4) + ($team->intelligence * 5)
But this is not smooth enough. We can do better.
I will create an Interface to specify the contract of the classes I’ll create;
interface StatCalculator
{
public function weight(): int;
public function calculate(): int;
}
Then I will create a simple IntelligenceClass;
class IntelligenceClass implements StatCalculator
{
protected $stat;
public function __construct(int $stat)
{
$this->stat = $stat;
}
public function weight(): int
{
return 5;
}
public function calculate(): int
{
return $this->stat * $this->weight();
}
}
As you see I implemented StatCalculator Interface. This class is created just for the purpose of calculating a value with our given stat and default multiplier. We have two more classes like that but with a little difference.
class AgilityClass implements StatCalculator
{
protected $stat;
private $statCalculator;
public function __construct(StatCalculator $statCalculator, int $stat)
{
$this->stat = $stat;
$this->statCalculator = $statCalculator;
}
public function weight(): int
{
return 4;
}
public function calculate(): int
{
return ( $this->stat * $this->weight() ) + $this->statCalculator->calculate();
}
}
As you see I created Agility Class but with a little difference. I gave StatCalculator type value to construct for the purpose of giving the benefit of reaching to IntelligenceClass and its functions.
$teamPower = ( new AgilityClass(new IntelligenceClass($team->intelligence),$team->agility) )->calculate();
There is only one calculation for AgilityClass in here. But it will eventually trigger IntelligenceClass calculation method also.
So when we var_dump the value we must get (5 * 5) + (6 *4) = 49
var_dump($teamPower);
(int49)
So now we can go further with creating StrengthClass also.
class StrengthClass implements StatCalculator
{
protected $stat;
protected $statCalculator;
public function __construct(StatCalculator $statCalculator, int $stat)
{
$this->stat = $stat;
$this->statCalculator = $statCalculator;
}
public function weight(): int
{
return 3;
}
public function calculate(): int
{
return ( $this->stat * $this->weight() ) + $this->statCalculator->calculate();
}
}
As you see we did the same thing. So the function wil be like this.
$teamPower = (new StrengthClass( new AgilityClass(new IntelligenceClass($team->intelligence),$team->agility),$team->strength))->calculate();var_dump($teamPower);
int(70)
As you see the code started from Strength, then went to Agility calculate function and from there it went to Intelligence Class. We don’t have to stop here. We can put a calculate power in our Team class so it will be tidy and more understandable.
class Team
{
..........
public function calculatePower(): int
{
return ( new StrengthClass(new AgilityClass(new IntelligenceClass($this->intelligence), $this->agility), $this->strength) )->calculate();
}
}
So we can call this function from wherever we want like this;
$team = new Team();
$team->calculatePower();
This was a simple example for decorator pattern and maybe it is not the perfect example. But the main concept is pretty straight forward so I think anyone who reads this article will get the pattern’s worokflow.
There is good video about this pattern. If you have time you can look at it;