Press "Enter" to skip to content

Posts tagged as “design pattern”

Singleton (PHP)

Singleton is a creational design pattern, which ensures that only that object exists in the memory and provides a single point of access to it for any other code.

<?php

namespace JSTechRoad\Singleton;

// The Singleton class defines the `GetInstance` method 
// that serves as an alternative to constructor and lets 
// clients access the same instance of this class forever.
class Singleton
{
    // The Singleton's instance is stored in a static field
    private static $instances = [];

    // The Singleton's constructor should always be private
    // to prevent direct construction calls with the `new` operator.
    protected function __construct() { }

    // Singletons should not be cloneable.
    protected function __clone() { }

    // Singletons should not be restorable from strings.
    public function __wakeup()
    {
        throw new \Exception("Cannot unserialize a singleton.");
    }

    // static method that controls the access to the singleton
    // instance. 
    public static function getInstance(): Singleton
    {
        $cls = static::class;
        if (!isset(self::$instances[$cls])) {
            self::$instances[$cls] = new static();
        }

        return self::$instances[$cls];
    }

    // any singleton should define some business logic, which can be
    // executed on its instance.
    public function someFunction()
    {
        // ...
    }
}

// testing function
function clientCode()
{
    $s1 = Singleton::getInstance();
    $s2 = Singleton::getInstance();
    if ($s1 === $s2) {
        echo "Singleton works!";
    } else {
        echo "Singleton failed!";
    }
}

clientCode();

Prototype (Clone) Design in PHP

Prototype (or Clone) is a creational design pattern that lets you copy existing objects not dependent on their classes. No need to go through all the fields of the original object and copy their values over to the new object. Simple as one word: CLONE!

<?php

namespace JSTechRoad\Prototype;

// PHP has built-in cloning support.
// Note that the constructor won't be executed during cloning.
// If you have complex logic inside the constructor,
// you may need to execute it in the `__clone` method as well.
class Prototype
{
    public $primitive;
    public $component;
    public $circularReference;

    public function __clone()
    {
        $this->component = clone $this->component;
        $this->circularReference = clone $this->circularReference;
        $this->circularReference->prototype = $this;
    }
}

class ComponentWithBackReference
{
    public $prototype;

    public function __construct(Prototype $prototype)
    {
        $this->prototype = $prototype;
    }
}

function clientCode()
{
    $p1 = new Prototype();
    $p1->primitive = 100;
    $p1->component = new \DateTime();
    $p1->circularReference = new ComponentWithBackReference($p1);

    $p2 = clone $p1;
    if ($p1->primitive === $p2->primitive) {
        echo "Primitive field values have been carried over to a clone.\n";
    } 
    if ($p1->component === $p2->component) {
        echo "Simple component has not been cloned.\n";
    }
    if ($p1->circularReference === $p2->circularReference) {
        echo "Component with back reference has not been cloned.\n";
    }

    if ($p1->circularReference->prototype === $p2->circularReference->prototype) {
        echo "Component with back reference is linked to original object.\n";
    }
}

clientCode();

 

 

Abstract Factory (PHP)

Abstract Factory is a creational design pattern that lets you produce families of related objects without specifying their concrete classes.

Benefit:

  • You can be sure that the products you’re getting from a factory are compatible with each other.
  • You avoid tight coupling between concrete products and client code.
  • Single Responsibility Principle. You can extract the product creation code into one place, making the code easier to support.
  • Open/Closed Principle. You can introduce new variants of products without breaking existing client code.

Disadvantage:

  • The code may become more complicated since you need to introduce a lot of new subclasses to implement the pattern. The best case scenario is when you’re introducing the pattern into an existing hierarchy of creator classes.

Why?

For example. Our online furniture store has several variants of this family.

Products Chair + Sofa + Bed are available in these variants: Modern, Classic, Wooden.

A customer wants to order a Modern-style sofa. You need a way to create individual furniture objects so that they match other objects of the same family. You don’t want to change existing code when adding new products or families of products to the program. Furniture vendors update their catalogs very often, and you wouldn’t want to change the core code each time it happens.

Sample Code:

<?php

namespace JSTechRoad\AbstractFactory\Conceptual;

interface AbstractFactory
{
    public function createProductA(): AbstractProductA;

    public function createProductB(): AbstractProductB;
}

class ConcreteFactory1 implements AbstractFactory
{
    public function createProductA(): AbstractProductA
    {
        return new ConcreteProductA1();
    }

    public function createProductB(): AbstractProductB
    {
        return new ConcreteProductB1();
    }
}

class ConcreteFactory2 implements AbstractFactory
{
    public function createProductA(): AbstractProductA
    {
        return new ConcreteProductA2();
    }

    public function createProductB(): AbstractProductB
    {
        return new ConcreteProductB2();
    }
}

interface AbstractProductA
{
    public function usefulFunctionA(): string;
}

class ConcreteProductA1 implements AbstractProductA
{
    public function usefulFunctionA(): string
    {
        return "The result of the product A1.";
    }
}

class ConcreteProductA2 implements AbstractProductA
{
    public function usefulFunctionA(): string
    {
        return "The result of the product A2.";
    }
}

interface AbstractProductB
{
    /**
     * Product B is able to do its own thing...
     */
    public function usefulFunctionB(): string;

    /**
     * ...but it also can collaborate with the ProductA.
     *
     * The Abstract Factory makes sure that all products it creates are of the
     * same variant and thus, compatible.
     */
    public function anotherUsefulFunctionB(AbstractProductA $collaborator): string;
}

class ConcreteProductB1 implements AbstractProductB
{
    public function usefulFunctionB(): string
    {
        return "The result of the product B1.";
    }

    /**
     * The variant, Product B1, is only able to work correctly with the variant,
     * Product A1. Nevertheless, it accepts any instance of AbstractProductA as
     * an argument.
     */
    public function anotherUsefulFunctionB(AbstractProductA $collaborator): string
    {
        $result = $collaborator->usefulFunctionA();

        return "The result of the B1 collaborating with the ({$result})";
    }
}

class ConcreteProductB2 implements AbstractProductB
{
    public function usefulFunctionB(): string
    {
        return "The result of the product B2.";
    }

    /**
     * The variant, Product B2, is only able to work correctly with the variant,
     * Product A2. Nevertheless, it accepts any instance of AbstractProductA as
     * an argument.
     */
    public function anotherUsefulFunctionB(AbstractProductA $collaborator): string
    {
        $result = $collaborator->usefulFunctionA();

        return "The result of the B2 collaborating with the ({$result})";
    }
}


function clientCode(AbstractFactory $factory)
{
    $productA = $factory->createProductA();
    $productB = $factory->createProductB();

    echo $productB->usefulFunctionB() . "\n";
    echo $productB->anotherUsefulFunctionB($productA) . "\n";
}

// Testing
echo "Client: Testing client code with the first factory type:\n";
clientCode(new ConcreteFactory1());

echo "\n";

echo "Client: Testing the same client code with the second factory type:\n";
clientCode(new ConcreteFactory2());

 

 

Factory Method (PHP)

Factory Method is a creational design pattern that provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created.

Benefit:

  • You avoid tight coupling between the creator and the concrete products. Single Responsibility Principle.
  • You can move the product creation code into one place in the program, making the code easier to support. Open/Closed Principle.
  • You can introduce new types of products into the program without breaking existing client code.

Disadvantage:

  • The code may become more complicated since you need to introduce a lot of new subclasses to implement the pattern. The best case scenario is when you’re introducing the pattern into an existing hierarchy of creator classes.

Why?

Adding a new class to the program isn’t that simple if the rest of the code is already coupled to existing classes. For example, the original version of your app can only handle transportation by trucks, so the bulk of your code lives inside the Truck class.

After a while, your app becomes very popular. Suddenly, you receive dozens of requests from sea transportation companies to ask you to implement sea logistics into the app. (Ship class)

Sample code:

<?php

namespace RefactoringGuru\FactoryMethod\Conceptual;

abstract class Creator
{
    abstract public function factoryMethod(): Product;

    /**
     * Despite its name, the Creator's primary responsibility is
     * not creating products. Usually, it contains some core 
     * business logic that relies on Product objects,
     * returned by the factory method. Subclasses can
     * indirectly change that business logic by overriding
     * the factory method
     * and returning a different type of product from it.
     */
    public function someOperation(): string
    {
        // Call the factory method to create a Product object.
        $product = $this->factoryMethod();
        // Now, use the product.
        $result = "Creator: The same creator's code has just worked with " . $product->operation();

        return $result;
    }
}


class ConcreteCreator1 extends Creator
{
    public function factoryMethod(): Product
    {
        return new ConcreteProduct1();
    }
}

class ConcreteCreator2 extends Creator
{
    public function factoryMethod(): Product
    {
        return new ConcreteProduct2();
    }
}

/**
 * The Product interface declares the operations that all 
 * concrete products must implement.
 */
interface Product
{
    public function operation(): string;
}

/**
 * Concrete Products provide various implementations of the 
 * Product interface.
 */
class ConcreteProduct1 implements Product
{
    public function operation(): string
    {
        return "{Result of the ConcreteProduct1}";
    }
}

class ConcreteProduct2 implements Product
{
    public function operation(): string
    {
        return "{Result of the ConcreteProduct2}";
    }
}

/**
 * The client code works with an instance of a concrete 
 * creator, albeit through
 * its base interface. As long as the client keeps working 
 * with the creator via
 * the base interface, you can pass it any creator's subclass.
 */
function clientCode(Creator $creator)
{
    // ...
    echo "Client: I'm not aware of the creator's class, but it still works.\n" . $creator->someOperation();
    // ...
}

echo "App: Launched with the ConcreteCreator1.\n";
clientCode(new ConcreteCreator1());
echo "\n\n";

echo "App: Launched with the ConcreteCreator2.\n";
clientCode(new ConcreteCreator2());

 Output:

App: Launched with the ConcreteCreator1.
Client: I'm not aware of the creator's class, but it still works.
Creator: The same creator's code has just worked with {Result of the ConcreteProduct1}

App: Launched with the ConcreteCreator2.
Client: I'm not aware of the creator's class, but it still works.
Creator: The same creator's code has just worked with {Result of the ConcreteProduct2}