PHP設計模式範例 — DesignPatternsPHP(1)建立型設計模式

【搬運於GitHub開源項目DesignPatternsPHP】

項目地址: 戳我

一、建立型設計模式

在軟件工程中,建立型設計模式承擔着對象建立的職責,嘗試建立適合程序上下文的對象,對象建立設計模式的產生是因爲軟件工程設計的問題,具體說是向設計中增長複雜度,建立型設計模式解決了程序設計中對象建立的問題。php

1.1 抽象工廠

1.1.1 目的

建立一系列相關或依賴的對象,而不指定它們的具體類。一般建立的類都實現相同的接口。抽象工廠的客戶端並不關心這些對象是如何建立的,它只知道它們是如何組合在一塊兒的。前端

1.1.2 UML圖

clipboard.png

1.1.3 代碼

你能夠在 GitHub 上查看代碼git

Parser.phpgithub

<?php
namespace DesignPatterns\Creational\AbstractFactory;

interface Parser
{
    public function parse(string $input): array;
}

CsvParser.php數據庫

<?php

namespace DesignPatterns\Creational\AbstractFactory;

class CsvParser implements Parser
{
    const OPTION_CONTAINS_HEADER = true;
    const OPTION_CONTAINS_NO_HEADER = false;

    /**
     * @var bool
     */
    private $skipHeaderLine;

    public function __construct(bool $skipHeaderLine)
    {
        $this->skipHeaderLine = $skipHeaderLine;
    }

    public function parse(string $input): array
    {
        $headerWasParsed = false;
        $parsedLines = [];

        foreach (explode(PHP_EOL, $input) as $line) {
            if (!$headerWasParsed && $this->skipHeaderLine === self::OPTION_CONTAINS_HEADER) {
                $headerWasParsed = true;
                continue;
            }

            $parsedLines[] = str_getcsv($line);
        }

        return $parsedLines;
    }
}

JsonParser.phpjson

<?php

namespace DesignPatterns\Creational\AbstractFactory;

class JsonParser implements Parser
{
    public function parse(string $input): array
    {
        return json_decode($input, true);
    }
}

ParserFactory.phpsegmentfault

<?php

namespace DesignPatterns\Creational\AbstractFactory;

class ParserFactory
{
    public function createCsvParser(bool $skipHeaderLine): CsvParser
    {
        return new CsvParser($skipHeaderLine);
    }

    public function createJsonParser(): JsonParser
    {
        return new JsonParser();
    }
}

1.2 生成器模式

1.2.1 目的

生成器的目的是將複雜對象的建立過程(流程)進行抽象,生成器表現爲接口的形式。後端

在特定的狀況下,好比若是生成器對將要建立的對象有足夠多的瞭解,那麼表明生成器的接口 interface 能夠是一個抽象類(也就是說能夠有必定的具體實現,就像衆所周知的適配器模式)。設計模式

若是對象有複雜的繼承樹,理論上建立對象的生成器也一樣具備複雜的繼承樹。緩存

提示:生成器一般具備流暢的接口,推薦閱讀關於 PHPUnitmock 生成器獲取更好的理解。

1.2.2 例子

  • PHPUnit: Mock 生成器

1.2.3 UML圖

clipboard.png

1.2.4 代碼

你能夠在 GitHub 上找到這些代碼

Director.php

<?php

namespace DesignPatterns\Creational\Builder;

use DesignPatterns\Creational\Builder\Parts\Vehicle;

/**
 * Director is part of the builder pattern. It knows the interface of the builder
 * and builds a complex object with the help of the builder
 *
 * You can also inject many builders instead of one to build more complex objects
 */
class Director
{
    public function build(BuilderInterface $builder): Vehicle
    {
        $builder->createVehicle();
        $builder->addDoors();
        $builder->addEngine();
        $builder->addWheel();

        return $builder->getVehicle();
    }
}

BuilderInterface.php

<?php

namespace DesignPatterns\Creational\Builder;

use DesignPatterns\Creational\Builder\Parts\Vehicle;

interface BuilderInterface
{
    public function createVehicle();

    public function addWheel();

    public function addEngine();

    public function addDoors();

    public function getVehicle(): Vehicle;
}

TruckBuilder.php

<?php

namespace DesignPatterns\Creational\Builder;

use DesignPatterns\Creational\Builder\Parts\Vehicle;

class TruckBuilder implements BuilderInterface
{
    /**
     * @var Parts\Truck
     */
    private $truck;

    public function addDoors()
    {
        $this->truck->setPart('rightDoor', new Parts\Door());
        $this->truck->setPart('leftDoor', new Parts\Door());
    }

    public function addEngine()
    {
        $this->truck->setPart('truckEngine', new Parts\Engine());
    }

    public function addWheel()
    {
        $this->truck->setPart('wheel1', new Parts\Wheel());
        $this->truck->setPart('wheel2', new Parts\Wheel());
        $this->truck->setPart('wheel3', new Parts\Wheel());
        $this->truck->setPart('wheel4', new Parts\Wheel());
        $this->truck->setPart('wheel5', new Parts\Wheel());
        $this->truck->setPart('wheel6', new Parts\Wheel());
    }

    public function createVehicle()
    {
        $this->truck = new Parts\Truck();
    }

    public function getVehicle(): Vehicle
    {
        return $this->truck;
    }
}

CarBuilder.php

<?php

namespace DesignPatterns\Creational\Builder;

use DesignPatterns\Creational\Builder\Parts\Vehicle;

class CarBuilder implements BuilderInterface
{
    /**
     * @var Parts\Car
     */
    private $car;

    public function addDoors()
    {
        $this->car->setPart('rightDoor', new Parts\Door());
        $this->car->setPart('leftDoor', new Parts\Door());
        $this->car->setPart('trunkLid', new Parts\Door());
    }

    public function addEngine()
    {
        $this->car->setPart('engine', new Parts\Engine());
    }

    public function addWheel()
    {
        $this->car->setPart('wheelLF', new Parts\Wheel());
        $this->car->setPart('wheelRF', new Parts\Wheel());
        $this->car->setPart('wheelLR', new Parts\Wheel());
        $this->car->setPart('wheelRR', new Parts\Wheel());
    }

    public function createVehicle()
    {
        $this->car = new Parts\Car();
    }

    public function getVehicle(): Vehicle
    {
        return $this->car;
    }
}

Parts/Vehicle.php

<?php

namespace DesignPatterns\Creational\Builder\Parts;

abstract class Vehicle
{
    /**
     * @var object[]
     */
    private $data = [];

    /**
     * @param string $key
     * @param object $value
     */
    public function setPart($key, $value)
    {
        $this->data[$key] = $value;
    }
}

Parts/Truck.php

<?php

namespace DesignPatterns\Creational\Builder\Parts;

class Truck extends Vehicle
{
}

Parts/Car.php

<?php

namespace DesignPatterns\Creational\Builder\Parts;

class Engine
{
}

Parts/Engine.php

<?php

namespace DesignPatterns\Creational\Builder\Parts;

class Engine
{
}

Parts/Wheel.php

<?php

namespace DesignPatterns\Creational\Builder\Parts;

class Wheel
{
}

Parts/Door.php

<?php

namespace DesignPatterns\Creational\Builder\Parts;

class Door
{
}

1.3 工廠方法

1.3.1 目的

SimpleFactory的優勢是您能夠子類化它來實現建立對象的不一樣方法。

對於簡單的狀況,這個抽象類可能只是一個接口。

這個模式是一個 "真正" 的設計模式,由於它遵循了依賴反轉原則 Dependency Inversion Principle 衆所周知這個 "D" 表明了真正的面向對象程序設計。

它意味着工廠方法類依賴於類的抽象,而不是具體將被建立的類,這是工廠方法模式與簡單工廠模式和靜態工廠模式最重要的區別。

1.3.2 UML圖

clipboard.png

1.3.3 代碼

你能夠在 GitHub 上找到這些代碼

Logger.php

<?php

namespace DesignPatterns\Creational\FactoryMethod;

interface Logger
{
    public function log(string $message);
}

StdoutLogger.php

<?php

namespace DesignPatterns\Creational\FactoryMethod;

class StdoutLogger implements Logger
{
    public function log(string $message)
    {
        echo $message;
    }
}

FileLogger.php

<?php

namespace DesignPatterns\Creational\FactoryMethod;

class FileLogger implements Logger
{
    /**
     * @var string
     */
    private $filePath;

    public function __construct(string $filePath)
    {
        $this->filePath = $filePath;
    }

    public function log(string $message)
    {
        file_put_contents($this->filePath, $message . PHP_EOL, FILE_APPEND);
    }
}

LoggerFactory.php

<?php

namespace DesignPatterns\Creational\FactoryMethod;

interface LoggerFactory
{
    public function createLogger(): Logger;
}

StdoutLoggerFactory.php

<?php

namespace DesignPatterns\Creational\FactoryMethod;

class StdoutLoggerFactory implements LoggerFactory
{
    public function createLogger(): Logger
    {
        return new StdoutLogger();
    }
}

FileLoggerFactory.php

<?php

namespace DesignPatterns\Creational\FactoryMethod;

class FileLoggerFactory implements LoggerFactory
{
    /**
     * @var string
     */
    private $filePath;

    public function __construct(string $filePath)
    {
        $this->filePath = $filePath;
    }

    public function createLogger(): Logger
    {
        return new FileLogger($this->filePath);
    }
}

1.4 多例

多例模式已經被考慮列入到反模式中!請使用依賴注入得到更好的代碼可測試性和可控性!

1.4.1 目的

使類僅有一個命名的對象的集合可供使用,像單例模式可是有多個實例。

1.4.2 例子

  • 2 個數據庫鏈接,好比,一個鏈接MySQL,另外一個鏈接SQLite
  • 多個日誌記錄器(一個記錄調試信息,另外一個記錄錯誤信息)

1.4.3 UML 圖

clipboard.png

1.4.4 代碼

你能夠在 GitHub 上找到這些代碼

Multiton.php

<?php

namespace DesignPatterns\Creational\Multiton;

final class Multiton
{
    const INSTANCE_1 = '1';
    const INSTANCE_2 = '2';

    /**
     * @var Multiton[]
     */
    private static $instances = [];

    /**
     * this is private to prevent from creating arbitrary instances
     */
    private function __construct()
    {
    }

    public static function getInstance(string $instanceName): Multiton
    {
        if (!isset(self::$instances[$instanceName])) {
            self::$instances[$instanceName] = new self();
        }

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

    /**
     * prevent instance from being cloned
     */
    private function __clone()
    {
    }

    /**
     * prevent instance from being unserialized
     */
    private function __wakeup()
    {
    }
}

1.5 對象池

1.5.1 目的

對象池設計模式 是建立型設計模式,它會對新建立的對象應用一系列的初始化操做,讓對象保持當即可以使用的狀態 - 一個存放對象的 「池子」 - 而不是對對象進行一次性的的使用(建立並使用,完成以後當即銷燬)。對象池的使用者會對對象池發起請求,以指望獲取一個對象,並使用獲取到的對象進行一系列操做,當使用者對對象的使用完成以後,使用者會將由對象池的對象建立工廠建立的對象返回給對象池,而不是用完以後銷燬獲取到的對象。

對象池在某些狀況下會帶來重要的性能提高,好比耗費資源的對象初始化操做,實例化類的代價很高,但每次實例化的數量較少的狀況下。對象池中將被建立的對象會在真正被使用時被提早建立,避免在使用時讓使用者浪費對象建立所需的大量時間(好比在對象某些操做須要訪問網絡資源的狀況下)從池子中取得對象的時間是可預測的,但新建一個實例所需的時間是不肯定。

總之,對象池會爲你節省寶貴的程序執行時間,好比像數據庫鏈接,socket鏈接,大量耗費資源的表明數字資源的對象,像字體或者位圖。不過,在特定狀況下,簡單的對象建立池(沒有請求外部的資源,僅僅將自身保存在內存中)或許並不會提高效率和性能,這時候,就須要使用者酌情考慮了。

1.5.2 UML圖

clipboard.png

1.5.3 代碼

你能夠在 GitHub 上找到這些代碼

WorkerPool.php

<?php

namespace DesignPatterns\Creational\Pool;

class WorkerPool implements \Countable
{
    /**
     * @var StringReverseWorker[]
     */
    private $occupiedWorkers = [];

    /**
     * @var StringReverseWorker[]
     */
    private $freeWorkers = [];

    public function get(): StringReverseWorker
    {
        if (count($this->freeWorkers) == 0) {
            $worker = new StringReverseWorker();
        } else {
            $worker = array_pop($this->freeWorkers);
        }

        $this->occupiedWorkers[spl_object_hash($worker)] = $worker;

        return $worker;
    }

    public function dispose(StringReverseWorker $worker)
    {
        $key = spl_object_hash($worker);

        if (isset($this->occupiedWorkers[$key])) {
            unset($this->occupiedWorkers[$key]);
            $this->freeWorkers[$key] = $worker;
        }
    }

    public function count(): int
    {
        return count($this->occupiedWorkers) + count($this->freeWorkers);
    }
}

StringReverseWorker.php

<?php

namespace DesignPatterns\Creational\Pool;

class StringReverseWorker
{
    /**
     * @var \DateTime
     */
    private $createdAt;

    public function __construct()
    {
        $this->createdAt = new \DateTime();
    }

    public function run(string $text)
    {
        return strrev($text);
    }
}

1.6 原型模式

1.6.1 目的

經過建立一個原型對象,而後複製原型對象來避免經過標準的方式建立大量的對象產生的開銷(new Foo())。

1.6.2 例子

  • 大量的數據對象(好比經過ORM獲取1,000,000行數據庫記錄而後建立每一條記錄對應的對象實體)

1.6.3 UML圖

clipboard.png

1.6.4 代碼

你能夠在 GitHub 上找到這些代碼

BookPrototype.php

<?php

namespace DesignPatterns\Creational\Prototype;

abstract class BookPrototype
{
    /**
     * @var string
     */
    protected $title;

    /**
     * @var string
     */
    protected $category;

    abstract public function __clone();

    public function getTitle(): string
    {
        return $this->title;
    }

    public function setTitle($title)
    {
        $this->title = $title;
    }
}

BarBookPrototype.php

<?php

namespace DesignPatterns\Creational\Prototype;

class BarBookPrototype extends BookPrototype
{
    /**
     * @var string
     */
    protected $category = 'Bar';

    public function __clone()
    {
    }
}

FooBookPrototype.php

<?php

namespace DesignPatterns\Creational\Prototype;

class FooBookPrototype extends BookPrototype
{
    /**
     * @var string
     */
    protected $category = 'Foo';

    public function __clone()
    {
    }
}

1.7 簡單工廠

1.7.1 目的

它與靜態工廠不一樣,由於它不是靜態的。所以,能夠有多個參數化的工廠,能夠子類化它,也能夠模擬它。它老是比靜態工廠更受歡迎!

1.7.2 UML圖

clipboard.png

1.7.3 代碼

你能夠在 GitHub 上找到這些代碼

SimpleFactory.php

<?php

namespace DesignPatterns\Creational\SimpleFactory;

class SimpleFactory
{
    public function createBicycle(): Bicycle
    {
        return new Bicycle();
    }
}

Bicycle.php

<?php

namespace DesignPatterns\Creational\SimpleFactory;

class Bicycle
{
    public function driveTo(string $destination)
    {
    }
}

1.7.4 使用

$factory = new SimpleFactory();
 $bicycle = $factory->createBicycle();
 $bicycle->driveTo('Paris');

1.8 單例模式

1.8.1 目標

使應用中只存在一個對象的實例,而且使這個單實例負責全部對該對象的調用。

1.8.2 例子

  • 數據庫鏈接器
  • 日誌記錄器 (可能有多個實例,好比有多個日誌文件由於不一樣的目的記錄不一樣到的日誌)
  • 應用鎖文件 (理論上整個應用只有一個鎖文件)

1.8.3 UML圖

clipboard.png

1.8.4 代碼

你能夠在 GitHub 上找到這些代碼

Singleton.php

<?php

namespace DesignPatterns\Creational\Singleton;

final class Singleton
{
    /**
     * @var Singleton
     */
    private static $instance;

    /**
     * gets the instance via lazy initialization (created on first usage)
     */
    public static function getInstance(): Singleton
    {
        if (null === static::$instance) {
            static::$instance = new static();
        }

        return static::$instance;
    }

    /**
     * is not allowed to call from outside to prevent from creating multiple instances,
     * to use the singleton, you have to obtain the instance from Singleton::getInstance() instead
     */
    private function __construct()
    {
    }

    /**
     * prevent the instance from being cloned (which would create a second instance of it)
     */
    private function __clone()
    {
    }

    /**
     * prevent from being unserialized (which would create a second instance of it)
     */
    private function __wakeup()
    {
    }
}

1.9 靜態工廠

1.9.1 目的

和抽象工廠相似,靜態工廠模式用來建立一系列互相關聯或依賴的對象,和抽象工廠模式不一樣的是靜態工廠模式只用一個靜態方法就解決了全部類型的對象建立,一般被命名爲 Factory 或者 Generators

1.9.2 例子

  • Zend Framework: zend_cache_ 後端或 _Frontend 使用工廠方法建立緩存後端和前端

1.9.3 UML圖

clipboard.png

1.9.4 代碼

你能夠在 GitHub 上找到這些代碼

StaticFactory.php

<?php

namespace DesignPatterns\Creational\StaticFactory;

/**
 * Note1: Remember, static means global state which is evil because it can't be mocked for tests
 * Note2: Cannot be subclassed or mock-upped or have multiple different instances.
 */
final class StaticFactory
{
    /**
     * @param string $type
     *
     * @return Formatter
     */
    public static function factory(string $type): Formatter
    {
        if ($type == 'number') {
            return new FormatNumber();
        } elseif ($type == 'string') {
            return new FormatString();
        }

        throw new \InvalidArgumentException('Unknown format given');
    }
}

Formatter.php

<?php

namespace DesignPatterns\Creational\StaticFactory;

interface Formatter
{
    public function format(string $input): string;
}

FormatString.php

<?php

namespace DesignPatterns\Creational\StaticFactory;

class FormatString implements Formatter
{
    public function format(string $input): string
    {
        return $input;
    }
}

FormatNumber.php

<?php

namespace DesignPatterns\Creational\StaticFactory;

class FormatNumber implements Formatter
{
    public function format(string $input): string
    {
        return number_format($input);
    }
}

相關文章:
PHP設計模式範例 — DesignPatternsPHP(2)結構型設計模式

相關文章
相關標籤/搜索