乐趣区

PHP设计模式范例 — DesignPatternsPHP(1)创建型设计模式

【搬运于 GitHub 开源项目 DesignPatternsPHP】
项目地址:戳我

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 例子

2 个数据库连接,比如,一个连接 MySQL,另一个连接 SQLite

多个日志记录器(一个记录调试信息,另一个记录错误信息)

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 代码
你可以在 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);
}
}

退出移动版