Strategy pattern uses a group of similar algorithms and make those algorithms interchangeable. The client can use any of the algorithms at any given time and also can change the algorithm when needed.
The algorithms used in strategy has the same interface implemented, but the internal working is different.
This article demonstrates Strategy pattern implementations in PHP. Check the following examples.
Implementation
Here are the steps for strategy pattern implementation in PHP-
Here is a simple example of Strategy pattern implementation in PHP-
<?php
// Strategy pattern PHP
// Strategy interface
interface IStrategy {
function operation1(): void;
function operation2(): void;
}
// First concrete strategy
class ConcreteStrategyOne implements IStrategy {
public function operation1(): void {
echo "Strategy One: operation 1n";
}
public function operation2(): void {
echo "Strategy One: operation 2n";
}
}
// Second concrete strategy
class ConcreteStrategyTwo implements IStrategy {
public function operation1(): void {
echo "Strategy Two: operation 1n";
}
public function operation2(): void {
echo "Strategy Two: operation 2n";
}
}
// Strategy class
class Strategy {
public function __construct(private IStrategy $strategy) {
}
public function setStrategy(IStrategy $strategy) {
$this->strategy = $strategy;
}
public function someOperation1(): void {
// perform some operation as per requirement (if required)
$this->strategy->operation1();
// perform some operation as per requirement (if required)
}
public function someOperation2(): void {
// perform some operation as per requirement (if required)
$this->strategy->operation2();
// perform some operation as per requirement (if required)
}
public function someComplexOperation(): void {
// perform some operation as per requirement (if required)
$this->strategy->operation2();
// perform some operation as per requirement (if required)
$this->strategy->operation1();
// perform some operation as per requirement (if required)
}
}
// Demo
echo "Using Strategy Onen";
$strategy = new Strategy(new ConcreteStrategyOne());
$strategy->someOperation1();
echo "Using Strategy Twon";
// Change strategy by setting a new strategy
$strategy->setStrategy(new ConcreteStrategyTwo());
$strategy->someOperation1();
$strategy->someOperation2();
$strategy->someComplexOperation();
Output will be as below-
Using Strategy One
Strategy One: operation 1
Using Strategy Two
Strategy Two: operation 1
Strategy Two: operation 2
Strategy Two: operation 2
Strategy Two: operation 1
Examples
Check the following examples of Strategy pattern implementation.
Example #1: Transport
In this example, we have a few transports like the bus, train, plane, etc. We want to implement the strategy pattern so that we can use any transport in the client and use functionality of that transport easily.
Transport Interface
<?php
// Transport.php
namespace BigBoxCodeDesignPatternStrategyTransport;
interface Transport {
function start(): void;
function stop(): void;
function repair(): void;
function getInfo(): void;
function operate(): void;
}
Bike Class [Transport]
- Create file “Bike.php”.
- Define transport class “Bike”.
- Implement “Transport” interface for the class.
<?php
// Bike.php
namespace BigBoxCodeDesignPatternStrategyTransport;
class Bike implements Transport {
public function start(): void {
echo "Bike startedn";
}
public function stop(): void {
echo "Bike stoppedn";
}
public function repair(): void {
echo "Bike repairn";
}
public function getInfo(): void {
echo "Transport type: Biken";
echo "Number of wheels: 2n";
echo "Average Weight: 700 Poundsn";
}
public function operate(): void {
echo "Riding Bike ............n";
}
}
Car Class [Transport]
- Create file “Car.php”.
- Define transport class “Car” and implement “Transport” interface.
<?php
// Car.php
namespace BigBoxCodeDesignPatternStrategyTransport;
class Car implements Transport {
public function start(): void {
echo "Car startedn";
}
public function stop(): void {
echo "Car stoppedn";
}
public function repair(): void {
echo "Car repairn";
}
public function getInfo(): void {
echo "Transport type: Carn";
echo "Number of wheels: 4n";
echo "Average Weight: 4,000 Poundsn";
}
public function operate(): void {
echo "Driving car ............n";
}
}
Plane Class [Transport]
- Create file “Plane.php”.
- Define transport class “Plane” and implement “Transport” interface.
<?php
// Plane.php
namespace BigBoxCodeDesignPatternStrategyTransport;
class Plane implements Transport {
public function start(): void {
echo "Plane startedn";
}
public function stop(): void {
echo "Plane stoppedn";
}
public function repair(): void {
echo "Plane repairn";
}
public function getInfo(): void {
echo "Transport type: Planen";
echo "Number of wheels: 3n";
echo "Average Weight: 50,000 Poundsn";
}
public function operate(): void {
echo "Flying plane ............n";
}
}
Transport Strategy Class
- Create file “TransportStrategy.php”.
- Define class “TransportStrategy”.
- Define private property “$transport”, of type “Transport”(the interface).
- In the constructor accept the “$transport” param and set value of the property.
- Define method for strategy – “setStrategy”.
- Define method as per client requirement. In the method implementations use/call the methods from the “$transport” object.
<?php
// TransportStrategy.php
namespace BigBoxCodeDesignPatternStrategyTransport;
class TransportStrategy {
public function __construct(private Transport $transport) {
}
public function setStrategy(Transport $transport) {
$this->transport = $transport;
}
public function execute(): void {
$this->transport->start();
$this->transport->getInfo();
$this->transport->operate();
}
public function repair(): void {
$this->transport->repair();
}
public function stop(): void {
$this->transport->stop();
}
}
Demo
Create an object of “TransportStrategy”. Set any of the transport objects as algorithm while creating the object. Or we can change the strategy algorithm using “setStrategy” method.
<?php
// demo.php
require __DIR__ . '/../../vendor/autoload.php';
use BigBoxCodeDesignPatternStrategyTransportBike;
use BigBoxCodeDesignPatternStrategyTransportCar;
use BigBoxCodeDesignPatternStrategyTransportPlane;
use BigBoxCodeDesignPatternStrategyTransportTransportStrategy;
// Use bike
echo "Operating Bike:n";
$myTransport = new TransportStrategy(new Bike());
$myTransport->execute();
$myTransport->stop();
// Use car
echo "Operating Car:n";
$myTransport->setStrategy(new Car());
$myTransport->execute();
$myTransport->stop();
$myTransport->repair();
// Use plane
echo "Operating plane:n";
$myTransport = new TransportStrategy(new Plane());
$myTransport->execute();
$myTransport->stop();
Output
Output of the code will be as below-
Operating Bike:
Bike started
Transport type: Bike
Number of wheels: 2
Average Weight: 700 Pounds
Riding Bike ............
Bike stopped
Operating Car:
Car started
Transport type: Car
Number of wheels: 4
Average Weight: 4,000 Pounds
Driving car ............
Car stopped
Car repair
Operating plane:
Plane started
Transport type: Plane
Number of wheels: 3
Average Weight: 50,000 Pounds
Flying plane ............
Plane stopped
Example #2: Mathematical Calculations
Let’s check how we can use the strategy pattern for performing math operations like -addition, subtraction, multiplication, and division.
We will use the math strategy class to decide which operation we want to perform for the provided operands.
Calculation Interface
- Create file “Calculation.php”.
- Define interface “Calculation”.
- Declare method “execute” for performing the operation, which takes 2 parameters and returns the result.
<?php
// Calculation.php
namespace BigBoxCodeDesignPatternStrategyMathCalculation;
interface Calculation {
function execute(float $num1, float $num2): float;
}
Add Class [Calculation]
- Create file “Add.php”.
- Define class “Add”.
- Implement “Calculation” interface for the class. In the “execute” method implementation return the sum of the parameters.
<?php
// Add.php
namespace BigBoxCodeDesignPatternStrategyMathCalculation;
class Add implements Calculation {
public function execute(float $num1, float $num2): float {
return $num1 + $num2;
}
}
Subtract Class [Calculation]
- Create file “Subtract.php”.
- Define class “Subtract”.
- Implement “Calculation” interface for the class. In the “execute” method implementation return the substraction of the parameters.
<?php
// Subtract.php
namespace BigBoxCodeDesignPatternStrategyMathCalculation;
class Subtract implements Calculation {
public function execute(float $num1, float $num2): float {
return $num1 - $num2;
}
}
Multiply Class [Calculation]
- Create file “Multiply.php”.
- Define class “Multiply”.
- Implement “Calculation” interface for the class. In the “execute” method implementation return the multiplication of the parameters.
<?php
// Multiply.php
namespace BigBoxCodeDesignPatternStrategyMathCalculation;
class Multiply implements Calculation {
public function execute(float $num1, float $num2): float {
return $num1 * $num2;
}
}
Divide Class [Calculation]
- Create file “Divide.php”.
- Define class “Divide”.
- Implement “Calculation” interface for the class. In the “execute” method implementation return the division of the parameters.
<?php
// Divide.php
namespace BigBoxCodeDesignPatternStrategyMathCalculation;
class Divide implements Calculation {
public function execute(float $num1, float $num2): float {
return $num1 / $num2;
}
}
Calculation Strategy Class
- Create file “CalculationStrategy.php”.
- Define class “CalculationStrategy”.
- Define a private property “$calculation” of type of “Calculation” (the interface).
- Accept the “$calculation” as param and set in the constructor. You can add a setter method for the “$calculation” if you want.
- Define a method “execute” which accepts 2 parameters. In the method implementation call the “execute” method from “$calculation” and pass the params.
<?php
// CalculationStrategy.php
namespace BigBoxCodeDesignPatternStrategyMathCalculation;
class CalculationStrategy {
public function __construct(private Calculation $calculation) {
}
public function execute(float $num1, float $num2): float {
return $this->calculation->execute($num1, $num2);
}
}
Demo
In the client create an object of “CalculationStrategy” and pass any of the calculation algorithm objects(Add, Subtract, Multiply, Divide) while creating the strategy object. This will set the calculation algorithm.
Then we can call the “execute” method of the strategy to perform operations.
<?php
// demo.php
require __DIR__ . '/../../vendor/autoload.php';
use BigBoxCodeDesignPatternStrategyMathCalculationAdd;
use BigBoxCodeDesignPatternStrategyMathCalculationCalculationStrategy;
use BigBoxCodeDesignPatternStrategyMathCalculationDivide;
use BigBoxCodeDesignPatternStrategyMathCalculationMultiply;
use BigBoxCodeDesignPatternStrategyMathCalculationSubtract;
echo "Performing operation: ADDn";
$myCalculation = new CalculationStrategy(new Add());
$result = $myCalculation->execute(10, 5);
echo "10 + 5 = " . $result . "n";
echo "Performing operation: SUBTRACTn";
$myCalculation = new CalculationStrategy(new Subtract());
$result = $myCalculation->execute(10, 5);
echo "10 - 5 = " . $result . "n";
echo "Performing operation: MULTIPLYn";
$myCalculation = new CalculationStrategy(new Multiply());
$result = $myCalculation->execute(10, 5);
echo "10 * 5 = " . $result . "n";
echo "Performing operation: DIVIDEn";
$myCalculation = new CalculationStrategy(new Divide());
$result = $myCalculation->execute(10, 5);
echo "10 / 5 = " . $result . "n";
Result
The following output will be generated-
Performing operation: ADD
10 + 5 = 15
Performing operation: SUBTRACT
10 - 5 = 5
Performing operation: MULTIPLY
10 * 5 = 50
Performing operation: DIVIDE
10 / 5 = 2
Example #3: File Storage
In this example, we have a few different ways for data/file storage, like Local storage, AWS S3, Google drive, etc.
We want to implement the strategy pattern for the storage algorithm/method selection, so that we can change the storage method at any time during the implementation usage in client.
Storage Interface
- Create file “StorageSystem.php”.
- Define interface “StorageSystem”.
- Declare methods as per requirement. Here we have methods – “storeFile”, “retrieveFile”, and “printFileInfo”.
<?php
// StorageStrategy.php
namespace BigBoxCodeDesignPatternStrategyFileStorage;
interface StorageSystem {
function storeFile(string $tempPath): int;
function retrieveFile(int $fileId): string;
function printFileInfo(int $fileId): void;
}
Local Storage [Storage Class]
- Create file “LocalStorage.php”.
- Define class “LocalStorage”.
- Implement interface “StorageSystem” for the class. Define methods “storeFile”, “retrieveFile”, and “printFileInfo” as part of the the interface implementation.
<?php
// LocalStorage.php
namespace BigBoxCodeDesignPatternStrategyFileStorage;
class LocalStorage implements StorageSystem {
public function storeFile(string $tempPath): int {
// Code to store file here
// return the ID that is obtained from the database record or any other unique identifier.
// For demo purpose a random number is returned here
return rand(100, 999);
}
public function retrieveFile(int $fileId): string {
// Retrieve the url and return back
// Some dummy url is returned for demo purpose
return "https://bigboxcode.com/files/local/" . $fileId;
}
public function printFileInfo(int $fileId): void {
echo "Storage type: Local Storagen";
echo "File ID: " . $fileId . "n";
echo "File URL: " . $this->retrieveFile($fileId) . "n";
}
}
Google Drive Storage [Storage Class]
- Create file “GoogleDriveStorage.php”.
- Define class “GoogleDriveStorage”, and implement the interface “StorageSystem” for the class.
<?php
// GoogleDriveStorage.php
namespace BigBoxCodeDesignPatternStrategyFileStorage;
class GoogleDriveStorage implements StorageSystem {
public function storeFile(string $tempPath): int {
// Code to store file here
// return the ID that is obtained from the database record or any other unique identifier.
// For demo purpose a random number is returned here
return rand(1_000, 9_999);
}
public function retrieveFile(int $fileId): string {
// Retrieve the url and return back
// Some dummy url is returned for demo purpose
return "https://drive.google.com/file/d/1234_9K7654hu6RT_9j7JKY3fK/view/bigboxcode/" . $fileId;
}
public function printFileInfo(int $fileId): void {
echo "Storage type: Google Driven";
echo "File ID: " . $fileId . "n";
echo "File URL: " . $this->retrieveFile($fileId) . "n";
}
}
AWS S3 Class [Storage Class]
- Create file “S3Storage.php”.
- Define class “S3Storage”, and implement the interface “StorageSystem”.
<?php
// S3Storage.php
namespace BigBoxCodeDesignPatternStrategyFileStorage;
class S3Storage implements StorageSystem {
public function storeFile(string $tempPath): int {
// Code to store file here
// return the ID that is obtained from the database record or any other unique identifier.
// For demo purpose a random number is returned here
return rand(10_000, 99_999);
}
public function retrieveFile(int $fileId): string {
// Retrieve the url and return back
// Some dummy url is returned for demo purpose
return "https://bigboxcode.s3.amazonaws.com/pdf/" . $fileId . "/UC-0e7654338-5697-4f99-b33-d89h87g5gf4gwfg.pdf";
}
public function printFileInfo(int $fileId): void {
echo "Storage type: AWS S3n";
echo "File ID: " . $fileId . "n";
echo "File URL: " . $this->retrieveFile($fileId) . "n";
}
}
Storage Strategy Class
- Create file “StorageStrategy.php”.
- Define class “StorageStrategy”.
- Define private property “$storageSystem” of type “StorageSystem”. Accept and set the param in the constructor. Also define a setter method for the “$storageSystem” property, named “setStrategy”.
- Define method “uploadFile” for uploading the file. This method implementation uses the “storageFile” and “printFileInfo” methods from the “$storageSystem”.
- Define method “getFileUrl” for retrieving the URL/path of the file after upload. In the method implementation use the “retrieveFile” method from “$storageSystem”.
<?php
// StorageStrategy.php
namespace BigBoxCodeDesignPatternStrategyFileStorage;
class StorageStrategy {
public function __construct(private StorageSystem $storageSystem) {
}
public function setStrategy(StorageSystem $storageSystem) {
$this->storageSystem = $storageSystem;
}
public function uploadFile(string $tempPath): int {
$fileId = $this->storageSystem->storeFile($tempPath);
$this->storageSystem->printFileInfo($fileId);
return $fileId;
}
public function getFileUrl(int $fileId): string {
return $this->storageSystem->retrieveFile($fileId);
}
}
Demo
In the client create an object of the “StorageStrategy” class. Set the strategy in the strategy class constructor or by using the “setStrategy” method.
<?php
// demo.php
require __DIR__ . '/../../vendor/autoload.php';
use BigBoxCodeDesignPatternStrategyFileStorageGoogleDriveStorage;
use BigBoxCodeDesignPatternStrategyFileStorageLocalStorage;
use BigBoxCodeDesignPatternStrategyFileStorageS3Storage;
use BigBoxCodeDesignPatternStrategyFileStorageStorageStrategy;
// Use Local storage
echo "Using local storage-n";
$fileStorage = new StorageStrategy(new LocalStorage());
$fileStorage->uploadFile("/some-temp-path");
// Use car
echo "Using AWS S3-n";
$fileStorage->setStrategy(new S3Storage());
$fileStorage->uploadFile("/some-temp-path");
// Use plane
echo "Using Google Drive-n";
$fileStorage->setStrategy(new GoogleDriveStorage());
$fileStorage->uploadFile("/some-temp-path");
Output
Output will be as follows-
Using local storage-
Storage type: Local Storage
File ID: 841
File URL: https://bigboxcode.com/files/local/841
Using AWS S3-
Storage type: AWS S3
File ID: 11351
File URL: https://bigboxcode.s3.amazonaws.com/pdf/11351/UC-0e7654338-5697-4f99-b33-d89h87g5gf4gwfg.pdf
Using Google Drive-
Storage type: Google Drive
File ID: 9773
File URL: https://drive.google.com/file/d/1234_9K7654hu6RT_9j7JKY3fK/view/bigboxcode/9773
Source Code
Use the following link to get the source code:
Example | Source Code Link |
---|---|
Example #1: Transport | GitHub |
Example #2: Mathematical Calculations | GitHub |
Example #3: File Storage | GitHub |
Other Code Implementations
Use the following links to check Strategy pattern implementation in other programming languages.