【搬运于GitHub开源项目DesignPatternsPHP】项目地址:戳我1、创建型设计模式在软件工程中,创建型设计模式承担着对象创建的职责,尝试创建适合程序上下文的对象,对象创建设计模式的产生是由于软件工程设计的问题,具体说是向设计中增加复杂度,创建型设计模式解决了程序设计中对象创建的问题。1.1 抽象工厂1.1.1 目的创建一系列相关或依赖的对象,而不指定它们的具体类。通常创建的类都实现相同的接口。抽象工厂的客户端并不关心这些对象是如何创建的,它只知道它们是如何组合在一起的。1.1.2 UML图1.1.3 代码你可以在 GitHub 上查看代码Parser.php<?phpnamespace DesignPatterns\Creational\AbstractFactory;interface Parser{ public function parse(string $input): array;}CsvParser.php<?phpnamespace 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<?phpnamespace DesignPatterns\Creational\AbstractFactory;class JsonParser implements Parser{ public function parse(string $input): array { return json_decode($input, true); }}ParserFactory.php<?phpnamespace 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<?phpnamespace 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<?phpnamespace 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<?phpnamespace 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<?phpnamespace 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<?phpnamespace 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<?phpnamespace DesignPatterns\Creational\Builder\Parts;class Truck extends Vehicle{}Parts/Car.php<?phpnamespace DesignPatterns\Creational\Builder\Parts;class Engine{}Parts/Engine.php<?phpnamespace DesignPatterns\Creational\Builder\Parts;class Engine{}Parts/Wheel.php<?phpnamespace DesignPatterns\Creational\Builder\Parts;class Wheel{}Parts/Door.php<?phpnamespace 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<?phpnamespace DesignPatterns\Creational\FactoryMethod;interface Logger{ public function log(string $message);}StdoutLogger.php<?phpnamespace DesignPatterns\Creational\FactoryMethod;class StdoutLogger implements Logger{ public function log(string $message) { echo $message; }}FileLogger.php<?phpnamespace 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<?phpnamespace DesignPatterns\Creational\FactoryMethod;interface LoggerFactory{ public function createLogger(): Logger;}StdoutLoggerFactory.php<?phpnamespace DesignPatterns\Creational\FactoryMethod;class StdoutLoggerFactory implements LoggerFactory{ public function createLogger(): Logger { return new StdoutLogger(); }}FileLoggerFactory.php<?phpnamespace 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 图1.4.4 代码你可以在 GitHub 上找到这些代码Multiton.php<?phpnamespace 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<?phpnamespace 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<?phpnamespace 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 代码你可以在 GitHub 上找到这些代码BookPrototype.php<?phpnamespace 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<?phpnamespace DesignPatterns\Creational\Prototype;class BarBookPrototype extends BookPrototype{ /* * @var string / protected $category = ‘Bar’; public function __clone() { }}FooBookPrototype.php<?phpnamespace 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<?phpnamespace DesignPatterns\Creational\SimpleFactory;class SimpleFactory{ public function createBicycle(): Bicycle { return new Bicycle(); }}Bicycle.php<?phpnamespace 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<?phpnamespace 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 或者 Generators1.9.2 例子Zend Framework: zend_cache 后端或 _Frontend 使用工厂方法创建缓存后端和前端1.9.3 UML图1.9.4 代码你可以在 GitHub 上找到这些代码StaticFactory.php<?phpnamespace 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<?phpnamespace DesignPatterns\Creational\StaticFactory;interface Formatter{ public function format(string $input): string;}FormatString.php<?phpnamespace DesignPatterns\Creational\StaticFactory;class FormatString implements Formatter{ public function format(string $input): string { return $input; }}FormatNumber.php<?phpnamespace DesignPatterns\Creational\StaticFactory;class FormatNumber implements Formatter{ public function format(string $input): string { return number_format($input); }}
...