Design Pattern: Strategy Pattern in PHP

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-

  • Create an interface for all the strategy/algorithm classes.
  • Create class for each algorithm and implement the strategy interface.
  • Create the main strategy class. Declare a property to store the reference to a strategy object, and also define a method for setting/changing strategy. Define methods for operations required by the client, and in the method implementation use/call methods from the strategy object.
  • In the client create a strategy object first. Then set the strategy algorithm. Also, the strategy can be changed at any time using the set method for strategy.

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 1\n";
    }

    public function operation2(): void {
        echo "Strategy One: operation 2\n";
    }
}

// Second concrete strategy
class ConcreteStrategyTwo implements IStrategy {
    public function operation1(): void {
        echo "Strategy Two: operation 1\n";
    }

    public function operation2(): void {
        echo "Strategy Two: operation 2\n";
    }
}

// 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 One\n";

$strategy = new Strategy(new ConcreteStrategyOne());
$strategy->someOperation1();


echo "Using Strategy Two\n";

// 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

  • Create file “Transport.php”.
  • Define interface “Transport”.
  • Declare methods for different usages – “start”, “stop”, “repair”, “getInfo”, “operate”.
<?php
// Transport.php

namespace BigBoxCode\DesignPattern\Strategy\Transport;


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 BigBoxCode\DesignPattern\Strategy\Transport;


class Bike implements Transport {
    public function start(): void {
        echo "Bike started\n";
    }
    
    public function stop(): void {
        echo "Bike stopped\n";
    }

    public function repair(): void {
        echo "Bike repair\n";
    }

    public function getInfo(): void {
        echo "Transport type: Bike\n";
        echo "Number of wheels: 2\n";
        echo "Average Weight: 700 Pounds\n";
    }

    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 BigBoxCode\DesignPattern\Strategy\Transport;


class Car implements Transport {
    public function start(): void {
        echo "Car started\n";
    }

    public function stop(): void {
        echo "Car stopped\n";
    }

    public function repair(): void {
        echo "Car repair\n";
    }

    public function getInfo(): void {
        echo "Transport type: Car\n";
        echo "Number of wheels: 4\n";
        echo "Average Weight: 4,000 Pounds\n";
    }

    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 BigBoxCode\DesignPattern\Strategy\Transport;


class Plane implements Transport {
    public function start(): void {
        echo "Plane started\n";
    }

    public function stop(): void {
        echo "Plane stopped\n";
    }

    public function repair(): void {
        echo "Plane repair\n";
    }

    public function getInfo(): void {
        echo "Transport type: Plane\n";
        echo "Number of wheels: 3\n";
        echo "Average Weight: 50,000 Pounds\n";
    }

    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 BigBoxCode\DesignPattern\Strategy\Transport;


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 BigBoxCode\DesignPattern\Strategy\Transport\Bike;
use BigBoxCode\DesignPattern\Strategy\Transport\Car;
use BigBoxCode\DesignPattern\Strategy\Transport\Plane;
use BigBoxCode\DesignPattern\Strategy\Transport\TransportStrategy;

// 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 BigBoxCode\DesignPattern\Strategy\MathCalculation;


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 BigBoxCode\DesignPattern\Strategy\MathCalculation;


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 BigBoxCode\DesignPattern\Strategy\MathCalculation;


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 BigBoxCode\DesignPattern\Strategy\MathCalculation;


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 BigBoxCode\DesignPattern\Strategy\MathCalculation;


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 BigBoxCode\DesignPattern\Strategy\MathCalculation;


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 BigBoxCode\DesignPattern\Strategy\MathCalculation\Add;
use BigBoxCode\DesignPattern\Strategy\MathCalculation\CalculationStrategy;
use BigBoxCode\DesignPattern\Strategy\MathCalculation\Divide;
use BigBoxCode\DesignPattern\Strategy\MathCalculation\Multiply;
use BigBoxCode\DesignPattern\Strategy\MathCalculation\Subtract;


echo "Performing operation: ADD\n";

$myCalculation = new CalculationStrategy(new Add());
$result = $myCalculation->execute(10, 5);

echo "10 + 5 = " . $result . "\n";


echo "Performing operation: SUBTRACT\n";

$myCalculation = new CalculationStrategy(new Subtract());
$result = $myCalculation->execute(10, 5);

echo "10 - 5 = " . $result . "\n";


echo "Performing operation: MULTIPLY\n";

$myCalculation = new CalculationStrategy(new Multiply());
$result = $myCalculation->execute(10, 5);

echo "10 * 5 = " . $result . "\n";


echo "Performing operation: DIVIDE\n";

$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 BigBoxCode\DesignPattern\Strategy\FileStorage;


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 BigBoxCode\DesignPattern\Strategy\FileStorage;


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 Storage\n";
        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 BigBoxCode\DesignPattern\Strategy\FileStorage;

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 Drive\n";
        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 BigBoxCode\DesignPattern\Strategy\FileStorage;

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 S3\n";
        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 BigBoxCode\DesignPattern\Strategy\FileStorage;

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 BigBoxCode\DesignPattern\Strategy\FileStorage\GoogleDriveStorage;
use BigBoxCode\DesignPattern\Strategy\FileStorage\LocalStorage;
use BigBoxCode\DesignPattern\Strategy\FileStorage\S3Storage;
use BigBoxCode\DesignPattern\Strategy\FileStorage\StorageStrategy;

// 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:

Other Code Implementations

Use the following links to check Strategy pattern implementation in other programming languages.

Leave a Comment


The reCAPTCHA verification period has expired. Please reload the page.