Design Patterns in PHP

Design Patterns in PHP

0 112230
Design Patterns in PHP

Patterns in PHP. Today we will discuss design patterns in web development, more precisely – in PHP. Experienced developers are probably already familiar with many of them, but our article would be extremely useful for all developers. So, what is it – design patterns? Design Patterns aren’t analysis patterns, they are not descriptions of common structures like linked lists, nor are they particular application or framework designs. In fact, design patterns are “descriptions of communicating objects and classes that are customized to solve a general design problem in a particular context.” In other words, Design patterns provide a generic reusable solution to the programming problems that we encounter every day. Design patterns are not ready classes or libraries, that can be simply applied to your system, this is not a concrete solution that can be converted in to source code, design patterns are much more than that. They are patterns, or templates, that can be implemented to solve a problem in different particular situations.
Design patterns help to speed up the development, as the templates are proven and from the developer’s position, only implementation is required. Design patterns not only make software development faster, but also encapsulate big ideas in a simpler way. Also, be careful not to use them in wrong places in order to avoid unpleasant situations. In addition to the theory, we also give you the most abstract and simple examples of design patterns.

“Each pattern describes a problem which occurs over and over again … and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without doing it the same way twice.” – Christopher Alexander

By now there are 23 different known design patterns, and they can be separated into three categories by purpose:

  • Creational Patterns: used to construct objects such that they can be decoupled from their implementing system
  • Structural Patterns: used to form large object structures between many disparate objects
  • Behavioral Patterns: used to manage algorithms, relationships, and responsibilities between objects

Complete list of design patterns you can find below:

Purpose Design Pattern Aspect(s) that can vary
Creational Abstract Factory families of product objects
Builder how a composite object gets created
Factory Method subclass of object that is instantiated
Prototype class of object that is instantiated
Singleton the sole instance of a class

Structural Adapter interface to an object
Bridge implementation of an object
Composite structure and composition of an object
Decorator responsibilities of an object without subclassing
Facade interface to a subsystem
Flyweight storage costs of objects
Proxy how an object is accessed; its location

Behavioral Chain of Responsibility object that can fulfill a request
Command when and how a request is fulfilled
Interpreter grammar and interpretation of a language
Iterator how an aggregate’s elements are accessed, traversed
Mediator how and which objects interact with each other
Memento what private information is stored outside an object, and when
Observer number of objects that depend on another object; how the dependent objects stay up to date
State states of an object
Strategy an algorithm
Template Method steps of an algorithm
Visitor operations that can be applied to object(s) without changing their class(es)

And now, we can start overview some of the listed patterns

Singleton

This is one of the most popular patterns. When developing web applications, it often makes sense conceptually and architecturally to allow access to only one instance of a particular class (during runtime). The singleton pattern enables us to do this. Example:

<?php
/**
 * Singleton class
 */
final class Product
{
    /**
     * @var self
     */
    private static $instance;
    /**
     * @var mixed
     */
    public $mix;
    /**
     * Return self instance
     *
     * @return self
     */
    public static function getInstance() {
        if (!(self::$instance instanceof self)) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    private function __construct() {
    }
    private function __clone() {
    }
}
$firstProduct = Product::getInstance();
$secondProduct = Product::getInstance();
$firstProduct->mix = 'test';
$secondProduct->mix = 'example';
print_r($firstProduct->mix);
// example
print_r($secondProduct->mix);
// example

Multiton

Maybe someone will want to use a variety of singletons in your project:

<?php
abstract class FactoryAbstract {
    protected static $instances = array();
    public static function getInstance() {
        $className = static::getClassName();
        if (!(self::$instances[$className] instanceof $className)) {
            self::$instances[$className] = new $className();
        }
        return self::$instances[$className];
    }
    public static function removeInstance() {
        $className = static::getClassName();
        if (array_key_exists($className, self::$instances)) {
            unset(self::$instances[$className]);
        }
    }
    final protected static function getClassName() {
        return get_called_class();
    }
    protected function __construct() { }
    final protected function __clone() { }
}
abstract class Factory extends FactoryAbstract {
    final public static function getInstance() {
        return parent::getInstance();
    }
    final public static function removeInstance() {
        parent::removeInstance();
    }
}
// using:
class FirstProduct extends Factory {
    public $a = [];
}
class SecondProduct extends FirstProduct {
}
FirstProduct::getInstance()->a[] = 1;
SecondProduct::getInstance()->a[] = 2;
FirstProduct::getInstance()->a[] = 3;
SecondProduct::getInstance()->a[] = 4;
print_r(FirstProduct::getInstance()->a);
// array(1, 3)
print_r(SecondProduct::getInstance()->a);
// array(2, 4)

Strategy

The strategy pattern is based on algorithms. You encapsulate specific families of algorithms allowing the client class responsible for instantiating a particular algorithm to have no knowledge of the actual implementation. Example:

<?php
interface OutputInterface {
    public function load();
}
class SerializedArrayOutput implements OutputInterface {
    public function load() {
        return serialize($arrayOfData);
    }
}
class JsonStringOutput implements OutputInterface {
    public function load() {
        return json_encode($arrayOfData);
    }
}
class ArrayOutput implements OutputInterface {
    public function load() {
        return $arrayOfData;
    }
}

Decorator

This pattern allows us to add new or additional behavior to an object during runtime, depending on the situation. Example:

<?php
class HtmlTemplate {
    // any parent class methods
}
class Template1 extends HtmlTemplate {
    protected $_html;
    public function __construct() {
        $this->_html = "<p>__text__</p>";
    }
    public function set($html) {
        $this->_html = $html;
    }
    public function render() {
        echo $this->_html;
    }
}
class Template2 extends HtmlTemplate {
    protected $_element;
    public function __construct($s) {
        $this->_element = $s;
        $this->set("<h2>" . $this->_html . "</h2>");
    }
    public function __call($name, $args) {
        $this->_element->$name($args[0]);
    }
}
class Template3 extends HtmlTemplate {
    protected $_element;
    public function __construct($s) {
        $this->_element = $s;
        $this->set("<u>" . $this->_html . "</u>");
    }
    public function __call($name, $args) {
        $this->_element->$name($args[0]);
    }
}

Registry

This pattern is a bit unusual from the overall list, because it is not a Creational pattern. Well, register – it is hash, and you access to data through the static methods:

<?php
/**
* Registry class
*/
class Package {
    protected static $data = array();
    public static function set($key, $value) {
        self::$data[$key] = $value;
    }
    public static function get($key) {
        return isset(self::$data[$key]) ? self::$data[$key] : null;
    }
    final public static function removeObject($key) {
        if (array_key_exists($key, self::$data)) {
            unset(self::$data[$key]);
        }
    }
}
Package::set('name', 'Package name');
print_r(Package::get('name'));
// Package name

Factory

This is another very known pattern. It acts exactly as it sounds: this is class that does as the real factory of object instances. In other words, assume that we know that there are factories that produce some kind of a product. We do not care how a factory makes this product, but we know that any factory has one universal way to ask for it:

<?php
interface Factory {
    public function getProduct();
}
interface Product {
    public function getName();
}
class FirstFactory implements Factory {
    public function getProduct() {
        return new FirstProduct();
    }
}
class SecondFactory implements Factory {
    public function getProduct() {
        return new SecondProduct();
    }
}
class FirstProduct implements Product {
    public function getName() {
        return 'The first product';
    }
}
class SecondProduct implements Product {
    public function getName() {
        return 'Second product';
    }
}
$factory = new FirstFactory();
$firstProduct = $factory->getProduct();
$factory = new SecondFactory();
$secondProduct = $factory->getProduct();
print_r($firstProduct->getName());
// The first product
print_r($secondProduct->getName());
// Second product

Abstract Factory

There are situations when we have some of the same type of factories and we want to encapsulate the logic of choice, what of the factories use to a given task. This pattern cames to the rescue:

<?php
class Config {
    public static $factory = 1;
}
interface Product {
    public function getName();
}
abstract class AbstractFactory {
    public static function getFactory() {
        switch (Config::$factory) {
            case 1:
                return new FirstFactory();
            case 2:
                return new SecondFactory();
        }
        throw new Exception('Bad config');
    }
    abstract public function getProduct();
}
class FirstFactory extends AbstractFactory {
    public function getProduct() {
        return new FirstProduct();
    }
}
class FirstProduct implements Product {
    public function getName() {
        return 'The product from the first factory';
    }
}
class SecondFactory extends AbstractFactory {
    public function getProduct() {
        return new SecondProduct();
    }
}
class SecondProduct implements Product {
    public function getName() {
        return 'The product from second factory';
    }
}
$firstProduct = AbstractFactory::getFactory()->getProduct();
Config::$factory = 2;
$secondProduct = AbstractFactory::getFactory()->getProduct();
print_r($firstProduct->getName());
// The first product from the first factory
print_r($secondProduct->getName());
// Second product from second factory

Observer

An object is made observable by adding a method that allows another object, the observer to get registered. If the observable object gets changed, it sends a message to the objects which are registered as observers:

<?php
interface Observer {
  function onChanged($sender, $args);
}
interface Observable {
  function addObserver($observer);
}
class CustomerList implements Observable {
  private $_observers = array();
  public function addCustomer($name) {
    foreach($this->_observers as $obs)
      $obs->onChanged($this, $name);
  }
  public function addObserver($observer) {
    $this->_observers []= $observer;
  }
}
class CustomerListLogger implements Observer {
  public function onChanged($sender, $args) {
    echo( "'$args' Customer has been added to the list \n" );
  }
}
$ul = new UserList();
$ul->addObserver( new CustomerListLogger() );
$ul->addCustomer( "Jack" );

Adapter

This pattern allows you to repurpose a class with a different interface, allowing it to be used by a system which uses different calling methods:

<?php
class SimpleBook {
    private $author;
    private $title;
    function __construct($author_in, $title_in) {
        $this->author = $author_in;
        $this->title  = $title_in;
    }
    function getAuthor() {
        return $this->author;
    }
    function getTitle() {
        return $this->title;
    }
}
class BookAdapter {
    private $book;
    function __construct(SimpleBook $book_in) {
        $this->book = $book_in;
    }
    function getAuthorAndTitle() {
        return $this->book->getTitle().' by '.$this->book->getAuthor();
    }
}
// Usage
$book = new SimpleBook("Gamma, Helm, Johnson, and Vlissides", "Design Patterns");
$bookAdapter = new BookAdapter($book);
echo 'Author and Title: '.$bookAdapter->getAuthorAndTitle();
function echo $line_in) {
  echo $line_in."<br/>";
}

Lazy Initialization

Here is another interesting situation. Imagine that you have a factory, but you do not know what part of its functionality you need, and what – no. In such cases, the necessary operations are performed only if they are needed and only once:

<?php
interface Product {
    public function getName();
}
class Factory {
    protected $firstProduct;
    protected $secondProduct;
    public function getFirstProduct() {
        if (!$this->firstProduct) {
            $this->firstProduct = new FirstProduct();
        }
        return $this->firstProduct;
    }
    public function getSecondProduct() {
        if (!$this->secondProduct) {
            $this->secondProduct = new SecondProduct();
        }
        return $this->secondProduct;
    }
}
class FirstProduct implements Product {
    public function getName() {
        return 'The first product';
    }
}
class SecondProduct implements Product {
    public function getName() {
        return 'Second product';
    }
}
$factory = new Factory();
print_r($factory->getFirstProduct()->getName());
// The first product
print_r($factory->getSecondProduct()->getName());
// Second product
print_r($factory->getFirstProduct()->getName());
// The first product

Chain of responsibility

The pattern also has another name – Chain of Command. It follows a chain of command with a series of handlers. The message (query) runs through a series of these handlers and at each junction it is regulated whether the handler can handle the query or not. The process stops the moment a handler can handle the request:

<?php
interface Command {
    function onCommand($name, $args);
}
class CommandChain {
    private $_commands = array();
    public function addCommand($cmd) {
        $this->_commands[]= $cmd;
    }
    public function runCommand($name, $args) {
        foreach($this->_commands as $cmd) {
            if ($cmd->onCommand($name, $args))
                return;
        }
    }
}
class CustCommand implements Command {
    public function onCommand($name, $args) {
        if ($name != 'addCustomer')
            return false;
        echo("This is CustomerCommand handling 'addCustomer'\n");
        return true;
    }
}
class MailCommand implements Command {
    public function onCommand($name, $args) {
        if ($name != 'mail')
            return false;
        echo("This is MailCommand handling 'mail'\n");
        return true;
    }
}
$cc = new CommandChain();
$cc->addCommand( new CustCommand());
$cc->addCommand( new MailCommand());
$cc->runCommand('addCustomer', null);
$cc->runCommand('mail', null);

Object pool

Object pool – is a hash, which can be stacked to initialize an object and get them out if needed:

<?php
class Product {
    protected $id;
    public function __construct($id) {
        $this->id = $id;
    }
    public function getId() {
        return $this->id;
    }
}
class Factory {
    protected static $products = array();
    public static function pushProduct(Product $product) {
        self::$products[$product->getId()] = $product;
    }
    public static function getProduct($id) {
        return isset(self::$products[$id]) ? self::$products[$id] : null;
    }
    public static function removeProduct($id) {
        if (array_key_exists($id, self::$products)) {
            unset(self::$products[$id]);
        }
    }
}
Factory::pushProduct(new Product('first'));
Factory::pushProduct(new Product('second'));
print_r(Factory::getProduct('first')->getId());
// first
print_r(Factory::getProduct('second')->getId());
// second

Prototype

Sometime, some objects should be initialized multiple times. It makes sense to save on their initialization, especially if initialization requires time and resources. Prototype – a pre-initialized and saved object. If necessary, it could be cloned:

<?php
interface Product {
}
class Factory {
    private $product;
    public function __construct(Product $product) {
        $this->product = $product;
    }
    public function getProduct() {
        return clone $this->product;
    }
}
class SomeProduct implements Product {
    public $name;
}
$prototypeFactory = new Factory(new SomeProduct());
$firstProduct = $prototypeFactory->getProduct();
$firstProduct->name = 'The first product';
$secondProduct = $prototypeFactory->getProduct();
$secondProduct->name = 'Second product';
print_r($firstProduct->name);
// The first product
print_r($secondProduct->name);
// Second product

Builder

This template is used when we want to encapsulate the creation of a complex object:

<?php
class Product {
    private $name;
    public function setName($name) {
        $this->name = $name;
    }
    public function getName() {
        return $this->name;
    }
}
abstract class Builder {
    protected $product;
    final public function getProduct() {
        return $this->product;
    }
    public function buildProduct() {
        $this->product = new Product();
    }
}
class FirstBuilder extends Builder {
    public function buildProduct() {
        parent::buildProduct();
        $this->product->setName('The product of the first builder');
    }
}
class SecondBuilder extends Builder {
    public function buildProduct() {
        parent::buildProduct();
        $this->product->setName('The product of second builder');
    }
}
class Factory {
    private $builder;
    public function __construct(Builder $builder) {
        $this->builder = $builder;
        $this->builder->buildProduct();
    }
    public function getProduct() {
        return $this->builder->getProduct();
    }
}
$firstDirector = new Factory(new FirstBuilder());
$secondDirector = new Factory(new SecondBuilder());
print_r($firstDirector->getProduct()->getName());
// The product of the first builder
print_r($secondDirector->getProduct()->getName());
// The product of second builder

You may consider to unlock an additional table (bonus)

[sociallocker]

[/sociallocker]

SIMILAR ARTICLES


NO COMMENTS

Leave a Reply