PHP設計模式範例 — DesignPatternsPHP(1)建立型設計模式
【搬運於GitHub/">GitHub開源專案DesignPatternsPHP】
專案地址: ofollow,noindex" target="_blank">戳我
1、建立型設計模式
在軟體工程中,建立型設計模式承擔著物件建立的職責,嘗試建立適合程式上下文的物件,物件建立設計模式的產生是由於軟體工程設計的問題,具體說是向設計中增加複雜度,建立型設計模式解決了程式設計中物件建立的問題。
1.1 抽象工廠
1.1.1 目的
建立一系列相關或依賴的物件,而不指定它們的具體類。通常建立的類都實現相同的介面。抽象工廠的客戶端並不關心這些物件是如何建立的,它只知道它們是如何組合在一起的。
1.1.2 UML圖
1.1.3 程式碼
你可以在 GitHub 上檢視程式碼
Parser.php
<?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.php
<?php namespace DesignPatterns\Creational\AbstractFactory; class JsonParser implements Parser { public function parse(string $input): array { return json_decode($input, true); } }
ParserFactory.php
<?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
可以是一個抽象類(也就是說可以有一定的具體實現,就像眾所周知的介面卡模式)。
如果物件有複雜的繼承樹,理論上建立物件的生成器也同樣具有複雜的繼承樹。
提示:生成器通常具有流暢的介面,推薦閱讀關於 PHPUnit
的 mock
生成器獲取更好的理解。
1.2.2 例子
- PHPUnit: Mock 生成器
1.2.3 UML圖
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圖
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 例子
1.4.3 UML 圖
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圖
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圖
1.6.4 程式碼
你可以在 Prototype" rel="nofollow,noindex" target="_blank">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圖
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圖
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圖
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); } }