The strategy pattern is used when we need to execute some logic based on information provided at runtime. It is technically same as conditional statements such as if..else, select .. case so on. but OOP style. It is a great example for SOLID's the Open/Closed Principle. There can be a few ways of using this pattern but I'll just touch up on the most common variations.


Message publishing strategy


Classes


class MessagePublisher
{
private $strategy;

public function __construct(StrategyInterface $strategy)
{
$this->strategy = $strategy;
}

public function publish(string $message): string
{
return $this->strategy->process($message);
}
}

interface StrategyInterface
{
public function process(string $message): string;
}

class FacebookStrategy implements StrategyInterface
{
public function process(string $message): string
{
return sprintf('Message "%s" published on Facebook.', $message);
}
}

class TwitterStrategy implements StrategyInterface
{
public function process(string $message): string
{
return sprintf('Message "%s" published on Twitter.', $message);
}
}

Usage


require_once 'MessagePublisher.php';

$messagePublisher = new MessagePublisher(new FacebookStrategy());
echo $messagePublisher->publish('Hello');

echo PHP_EOL;

$messagePublisher = new MessagePublisher(new TwitterStrategy());
echo $messagePublisher->publish('Hello');

echo PHP_EOL;

Result


$ php oop/MessagePublisher/index.php 
Message "Hello" published on Facebook.
Message "Hello" published on Twitter.

Home painter strategy


Classes


class HomePainter
{
private $painters;

public function __construct(array $painters)
{
$this->painters = $painters;
}

public function start(Home $home, string $section): ?string
{
/** @var StrategyInterface $painter */
foreach ($this->painters as $painter) {
if ($painter->canPaint($section)) {
return $painter->paint($home);
}
}

return null;
}
}

class Home
{
public function doors(): string
{
return 'Doors painted yellow';
}

public function steps(): string
{
return 'Steps painted blue';
}
}

interface StrategyInterface
{
public function canPaint(string $section): bool;

public function paint(Home $home): string;
}

class YellowPainterStrategy implements StrategyInterface
{
public function canPaint(string $section): bool
{
return 'doors' === $section;
}

public function paint(Home $home): string
{
return $home->doors();
}
}

class BluePainterStrategy implements StrategyInterface
{
public function canPaint(string $section): bool
{
return 'steps' === $section;
}

public function paint(Home $home): string
{
return $home->steps();
}
}

Usage


require_once 'HomePainter.php';

$home = new Home();

$homePainter = new HomePainter([
new YellowPainterStrategy(),
new BluePainterStrategy(),
]);

echo $homePainter->start($home, 'doors');

echo PHP_EOL;

echo $homePainter->start($home, 'steps');

echo PHP_EOL;

Result


$ php oop/HomePainter/index.php 
Doors painted yellow
Steps painted blue

Number processor strategy


Classes


class NumberProcessor
{
private $strategies;

public function addProcessor(StrategyInterface $strategy)
{
$this->strategies[] = $strategy;

return $this;
}

public function run(int $number): int
{
/** @var StrategyInterface $strategy */
foreach ($this->strategies as $strategy) {
$number = $strategy->process($number);
}

return $number;
}
}

interface StrategyInterface
{
public function process(int $number): int;
}

class SumStrategy implements StrategyInterface
{
public function process(int $number): int
{
return $number + $number;
}
}

class MultiplyStrategy implements StrategyInterface
{
public function process(int $number): int
{
return $number * $number;
}
}

Usage


require_once 'NumberProcessor.php';

$numberProcessor = new NumberProcessor();
$numberProcessor
->addProcessor(new SumStrategy())
->addProcessor(new MultiplyStrategy());

echo $numberProcessor->run(4);

echo PHP_EOL;

Result


$ php oop/NumberProcessor/index.php 
64