Press "Enter" to skip to content

Posts published in “Creational 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();

 

 

Builder (PHP)

Builder is a creational design pattern that lets you construct complex objects step by step. The pattern allows you to produce different types and representations of an object using the same construction code. The intent of the Builder design pattern is to separate the construction of a complex object from its representation.

Benefit:

  • You can construct objects step-by-step, defer construction steps or run steps recursively.
  • You can reuse the same construction code when building various representations of products. Single Responsibility Principle.
  • You can isolate complex construction code from the business logic of the product.

Disadvantage:

  • The overall complexity of the code increases since the pattern requires creating multiple new classes.

Why?

Imagine a complex object that requires different attributes, step-by-step initialization of many fields, and nested objects. Such initialization code usually comes with lots of parameters inside a constructor.

For example, a house is a complex object to implement, some houses have swimming pools, some houses have garages…etc. Let see the following approach:

  • we can create a big constructor in the base House class with all possible parameters that control the house object. But end up most of the parameters will be unused, making the constructor calls really ugly.
  • we have the base House class, extend the base House class and create a set of subclasses to cover all combinations of the parameters. But eventually, end up with a lot of subclasses.

Sample Code:

<?php

namespace JSTechRoad\Builder;

interface Builder
{
    public function producePartA(): void;

    public function producePartB(): void;

    public function producePartC(): void;
}

/**
 * implementations of the building steps.
 */
class ConcreteBuilder1 implements Builder
{
    private $product;

    /**
     * A new builder instance should contain a blank product object
     */
    public function __construct()
    {
        $this->reset();
    }

    public function reset(): void
    {
        $this->product = new Product1();
    }

    /**
     * All production steps work with the same product instance.
     */
    public function producePartA(): void
    {
        $this->product->parts[] = "PartA1";
    }

    public function producePartB(): void
    {
        $this->product->parts[] = "PartB1";
    }

    public function producePartC(): void
    {
        $this->product->parts[] = "PartC1";
    }

    public function getProduct(): Product1
    {
        $result = $this->product;
        $this->reset();

        return $result;
    }
}

/**
 * Use the Builder pattern only when your products are very
 * complex and require extensive configuration. 
 */
class Product1
{
    public $parts = [];

    public function listParts(): void
    {
        echo "Product parts: " . implode(', ', $this->parts) . "\n\n";
    }
}

/**
 * The Director is only responsible for executing the 
 * building steps in a particular sequence. It is helpful 
 * when producing products according to a
 * specific order or configuration. Strictly speaking,
 * the Director class is
 * optional, since the client can control builders directly.
 */
class Director
{
    private $builder;

    /**
     * The Director works with any builder instance 
     * that the client code passes to it.
     */
    public function setBuilder(Builder $builder): void
    {
        $this->builder = $builder;
    }

    /**
     * The Director can construct several product 
     * variations using the same building steps.
     */
    public function buildMinimalViableProduct(): void
    {
        $this->builder->producePartA();
    }

    public function buildFullFeaturedProduct(): void
    {
        $this->builder->producePartA();
        $this->builder->producePartB();
        $this->builder->producePartC();
    }
}

function clientCode(Director $director)
{
    $builder = new ConcreteBuilder1();
    $director->setBuilder($builder);

    echo "Standard basic product:\n";
    $director->buildMinimalViableProduct();
    $builder->getProduct()->listParts();

    echo "Standard full featured product:\n";
    $director->buildFullFeaturedProduct();
    $builder->getProduct()->listParts();

    // The Builder pattern can be used without a Director class.
    echo "Custom product:\n";
    $builder->producePartA();
    $builder->producePartC();
    $builder->getProduct()->listParts();
}

$director = new Director();
clientCode($director);

 

 

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}