创建设计模式
单例模式(Singleton)
单例模式(Singleton)
:确保一个类只有一个实例化对象,并提供一个全局访问点。
常用于管理数据库连接、日志记录器、配置文件读取器等资源,这些资源只需要一个实例就足够了,并且需要在整个应用程序中被共享
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| class Singleton { private static $instance; private function __construct() {} private function __clone() {} private function __wakeup() {} public static function getInstance() { if (!self::$instance) { self::$instance = new self(); } return self::$instance; } public function doSomething() { } }
$instance1 = Singleton::getInstance(); $instance2 = Singleton::getInstance();
if ($instance1 === $instance2) { echo "它们是同一个实例"; } else { echo "它们不是同一个实例"; }
$instance1->doSomething();
|
工厂模式(Factory)
工厂模式(Factory)
:通过一个工厂类来创建对象,隐藏对象的创建细节。
提供了一种封装机制来创建对象,而无需在代码中直接实例化对象。工厂模式将对象的创建与使用解耦,使得代码更加灵活和可维护。
工厂模式的主要目标是实现对象的创建和使用分离,将对象的创建过程封装在一个独立的工厂类中。客户端(调用者)只需要知道工厂类和所需对象的接口,而不必关心对象的具体创建过程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| interface Product { public function useProduct(); }
class ConcreteProductA implements Product { public function useProduct() { echo "使用产品 A\n"; } } class ConcreteProductB implements Product { public function useProduct() { echo "使用产品 B\n"; } }
class ProductFactory { public static function createProduct($type) { if ($type == 'A') { return new ConcreteProductA(); } elseif ($type == 'B') { return new ConcreteProductB(); } else { throw new Exception('无效的产品类型'); } } }
$productA = ProductFactory::createProduct('A'); $productA->useProduct(); $productB = ProductFactory::createProduct('B'); $productB->useProduct();
try { $invalidProduct = ProductFactory::createProduct('C'); } catch (Exception $e) { echo $e->getMessage(); }
|
抽象工厂模式
抽象工厂模式(Abstract Factory)
:一个工厂类可以创建多种类型的相关对象。
是一种创建型设计模式,它提供了一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。抽象工厂模式允许客户端使用一致的接口来创建一组相关的产品对象,而无需关心这些对象的实际类。
比如:文件上传多种类型,比如图片、视频、音频等,都可以用相同的接口来上传,而具体的实现由不同的工厂来完成。
抽象工厂模式通常包含以下几个部分:
抽象工厂
:声明一个创建抽象产品对象的操作接口。
具体工厂
:实现抽象工厂接口,创建具体产品对象。
抽象产品
:定义产品的接口。
具体产品
:实现抽象产品的接口。
以下是一个简单的 PHP 抽象工厂模式的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
| interface AbstractProductA { public function useProductA(); }
class ConcreteProductA1 implements AbstractProductA { public function useProductA() { echo "使用产品A1\n"; } }
class ConcreteProductA2 implements AbstractProductA { public function useProductA() { echo "使用产品A2\n"; } }
interface AbstractProductB { public function useProductB(); }
class ConcreteProductB1 implements AbstractProductB { public function useProductB() { echo "使用产品B1\n"; } }
class ConcreteProductB2 implements AbstractProductB { public function useProductB() { echo "使用产品B2\n"; } }
interface AbstractFactory { public function createProductA(): AbstractProductA; public function createProductB(): AbstractProductB; }
class ConcreteFactory1 implements AbstractFactory { public function createProductA(): AbstractProductA { return new ConcreteProductA1(); } public function createProductB(): AbstractProductB { return new ConcreteProductB1(); } }
class ConcreteFactory2 implements AbstractFactory { public function createProductA(): AbstractProductA { return new ConcreteProductA2(); } public function createProductB(): AbstractProductB { return new ConcreteProductB2(); } }
$factory1 = new ConcreteFactory1(); $productA1 = $factory1->createProductA(); $productB1 = $factory1->createProductB(); $productA1->useProductA(); $productB1->useProductB(); $factory2 = new ConcreteFactory2(); $productA2 = $factory2->createProductA(); $productB2 = $factory2->createProductB(); $productA2->useProductA(); $productB2->useProductB();
|
在客户端代码中,我们可以根据需要选择使用哪个具体工厂来创建产品对象,并通过调用工厂对象的方法来获取具体产品对象。这种方式使得客户端代码与具体产品的实现解耦,增加了代码的灵活性和可维护性。
建造者模式
是一种对象构建的设计模式
,它允许你以更灵活的方式创建复杂对象
,而无需在构造函数
中指定所有需要的参数
定义
:封装一个复杂对象构造过程,并允许按步骤构造。
解释
:就是将复杂对象的创建过程拆分成多个简单对象的创建过程,并将这些简单对象组合起来构建出复杂对象。
角色组成
产品类
:表示被创建的复杂对象。它通常包含多个部分或者组成,并由具体的建造者逐步构建而成。
抽象建造者类
:定义了建造复杂对象所需要的各个部分的创建方法。它通常包括多个构建方法和一个返回产品的方法。
具体建造者类
:实现Builder接口,并提供各个部分或者组成的构建方法。
指挥者类
:负责控制建造者的构建顺序,指挥建造者如何构建复杂对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| interface Car { public function getParts(); }
class ConcreteCar implements Car { private $parts = []; public function addPart($part) { $this->parts[] = $part; } public function getParts() { return $this->parts; } }
interface CarBuilder { public function buildEngine(); public function buildBody(); public function buildDoors(); public function getCar(); }
class ConcreteCarBuilder implements CarBuilder { private $car; public function __construct() { $this->car = new ConcreteCar(); } public function buildEngine() { $this->car->addPart("Engine"); } public function buildBody() { $this->car->addPart("Body"); } public function buildDoors() { $this->car->addPart("Doors"); } public function getCar() { return $this->car; } }
class CarDirector { private $builder; public function __construct(CarBuilder $builder) { $this->builder = $builder; } public function constructCar() { $this->builder->buildEngine(); $this->builder->buildBody(); $this->builder->buildDoors(); } public function getCar() { return $this->builder->getCar(); } }
$builder = new ConcreteCarBuilder(); $director = new CarDirector($builder); $director->constructCar(); $car = $director->getCar(); print_r($car->getParts());
|
在这个示例中,
Car 接口
表示产品,
ConcreteCar
是具体的产品实现。
CarBuilder
是建造者接口,它定义了构建产品的各个部分的方法,
而 ConcreteCarBuilder
是具体的建造者实现。
CarDirector
是导演类,它指导如何构建产品。
客户端代码使用导演类和建造者类来创建并获取一个构建好的 Car 对象
.
原型模式
它允许一个对象通过复制其原型(即另一个已存在的对象)来创建新的对象,而不是通过类构造函数来实例化。
这种模式在某些场景下可能比直接实例化类更加灵活和高效,特别是在对象创建成本较高或者需要动态地改变对象创建过程时。
原型模式(Prototype)
:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
定义
:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
解释
:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
角色组成
抽象原型类
:声明一个克隆自身的接口。
具体原型类
:实现抽象原型类的克隆方法,并返回一个新的对象。
客户端类
:请求创建对象的类,它通过调用具体原型类的clone方法来创建新的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| <?php
abstract class Prototype { public function clone(): Prototype { return clone $this; } abstract public function operation(); }
class ConcretePrototype extends Prototype { private $data; public function __construct($data) { $this->data = $data; } public function operation(): void { echo "Data: " . $this->data . PHP_EOL; } }
$prototype = new ConcretePrototype("Hello, World!"); $prototype->operation();
$clone = $prototype->clone(); $clone->operation();
$clone->data = "Hello, Clone!"; $clone->operation(); $prototype->operation();
|
结构设计模式
适配器模式
将一个类的接口转换为客户端所期望的另一个接口。
常用于解决接口不兼容的问题,如将不同的数据库接口统一成相同的接口。
适配器模式通常包含以下三个角色:
目标接口
:客户端所期望的接口。
适配者
:现有需要适配的接口。
适配器
:将适配者的接口转换成目标接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| interface Target { public function request(); }
class Adaptee { public function specificRequest() { echo "Called specificRequest() in Adaptee."; } }
class Adapter implements Target { private $adaptee; public function __construct(Adaptee $adaptee) { $this->adaptee = $adaptee; } public function request() { echo "Called request() in Adapter."; $this->adaptee->specificRequest(); } }
$adaptee = new Adaptee(); $adapter = new Adapter($adaptee);
$adapter->request();
|
PHP适配器模式主要用于解决接口不兼容的问题,它可以在不修改原有代码逻辑的情况下,通过引入适配器类来连接不同的接口,实现代码的重用和扩展。
装饰器模式
- 动态地给一个对象添加额外的功能。
- 常用于需要为一个对象添加多个不同功能的情况,如增加
日志记录
、验证权限
等。
使用场景
- 当需要在不增加大量子类的情况下扩展类的功能。
- 当需要动态地添加或撤销对象的功能。
装饰器模式的主要组件包括
组件接口
:定义了一个对象的接口,可以给这些对象动态地添加职责。
具体组件
:实现了组件接口,是被装饰的对象。
装饰器接口
:继承了组件接口,可以持有对其他组件的引用。
具体装饰器
:实现了装饰器接口,并添加了对组件的额外职责
假设我们有一个简单的 Component 接口和一个 ConcreteComponent 类,以及一个 Decorator 类来装饰 ConcreteComponent。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| interface Component { public function operation(); } class ConcreteComponent implements Component { public function operation() { return "ConcreteComponent says: Hello, World!"; } } class Decorator implements Component { protected $component; public function __construct(Component $component) { $this->component = $component; } public function operation() { return $this->component->operation(); } } class ConcreteDecorator extends Decorator { public function operation() { $decoratedOutput = $this->component->operation(); return "ConcreteDecorator: " . $decoratedOutput; } }
$component = new ConcreteComponent(); $decorator = new ConcreteDecorator($component); echo $decorator->operation();
|
实际 PHP 开发中的使用场景
日志和跟踪
:你可以使用装饰器模式来在现有的类上添加日志或跟踪功能,而无需修改原始类的代码。
性能监控
:与日志类似,你可以使用装饰器来监控和记录方法的执行时间或其他性能相关的指标。
权限验证
:对于需要权限验证的操作,你可以使用装饰器来在方法执行前添加权限检查。
数据转换
:如果你需要在将数据返回给客户端之前对其进行某种转换(如加密、序列化等),可以使用装饰器模式。
UI 渲染
:在 Web 开发中,你可以使用装饰器来动态地添加或修改 HTML 元素的样式或行为。
缓存
:对于需要缓存的操作,你可以使用装饰器来在方法执行前检查缓存是否存在,如果存在则直接返回缓存结果,否则执行原始方法并将结果缓存起来。
桥接模式
- 将抽象部分与具体实现部分解耦。
- 常用于系统设计,以便可以独立地改变抽象和实现。
定义抽象接口
:首先,你需要定义一个抽象接口或抽象类,它包含了需要被实现的业务方法。这个接口或抽象类代表了抽象部分。
定义实现接口
:接着,你需要定义另一个接口或抽象类,它代表了实现部分。这个接口或抽象类会提供抽象接口中方法的具体实现。
具体实现类
:然后,你需要创建实现接口的具体实现类。这些类将实现接口中定义的方法,并提供具体的业务逻辑。
抽象类的子类
:你需要创建抽象类的子类(通常称为扩充抽象类),它持有对实现接口的引用,并调用实现接口的方法来完成业务逻辑。这个子类将抽象部分和实现部分连接起来,形成桥梁。
客户端调用
:最后,在客户端代码中,你需要创建扩充抽象类的实例,并将具体实现类的实例作为参数传递给扩充抽象类的构造函数。然后,你可以调用扩充抽象类的方法来执行业务逻辑。
以下是一个简单的 PHP 桥接模式示例,演示了如何将抽象部分和实现部分分离:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| interface People { public function setName(); }
class Man implements People { public function setName() { echo '我是张三'; } } class Woman implements People { public function setName() { echo '我是珍妮'; } }
abstract class Abstraction { public $instance; function __construct(People $instance) { $this->instance = $instance; } abstract public function get(); }
class Concrete extends Abstraction { public function get() { $this->instance->setName(); } }
$concrete = new Concrete(new Man()); $concrete->get();
|
行为设计模式
观察者模式
它允许对象(观察者)订阅另一个对象(主题或可观察者)的状态变化,并在状态发生变化时自动收到通知。这种设计模式广泛应用于实现事件驱动编程,以及当多个对象需要响应同一对象状态变化时。
- 定义了一种一对多的依赖关系,使得当被观察对象的状态发生变化时,所有依赖它的对象都能得到通知并自动更新。
- 常用于需要实现事件驱动的应用场景,如消息队列和订阅-发布系统。
观察者模式的主要角色包括:
主题(Subject)
:定义了添加、删除和通知观察者的接口。
观察者(Observer)
:定义了一个更新接口,以便在接收到通知时更新自己。
具体主题(ConcreteSubject)
:实现了主题接口,并维护了一个观察者列表,当状态发生变化时,会遍历列表并通知所有观察者。
具体观察者(ConcreteObserver)
:实现了观察者接口,并在更新方法中定义了自己的响应逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
| interface Observer { public function update($subject); }
class ConcreteObserverA implements Observer { public function update($subject) { echo "观察者 A 收到通知: " . $subject->getState() . "\n"; } }
class ConcreteObserverB implements Observer { public function update($subject) { echo "观察者 B 收到通知: " . $subject->getState() . "\n"; } }
interface Subject { public function attach(Observer $observer); public function detach(Observer $observer); public function notify(); public function setState($state); public function getState(); }
class ConcreteSubject implements Subject { private $state; private $observers = []; public function attach(Observer $observer) { $this->observers[] = $observer; } public function detach(Observer $observer) { $key = array_search($observer, $this->observers); if ($key !== false) { unset($this->observers[$key]); } } public function notify() { foreach ($this->observers as $observer) { $observer->update($this); } } public function setState($state) { $this->state = $state; $this->notify(); } public function getState() { return $this->state; } }
$subject = new ConcreteSubject(); $observerA = new ConcreteObserverA(); $observerB = new ConcreteObserverB(); $subject->attach($observerA); $subject->attach($observerB); $subject->setState("主题状态已更新");
|
在实时通信系统中,如聊天室、实时消息推送等,当有新消息产生时,需要实时通知所有在线用户。观察者模式可以很好地支持这种场景,将消息作为事件,将用户作为观察者,实现实时通信和消息传递的功能。
策略模式
- 定义了一系列算法,并将每个算法封装成一种策略,使得它们可以相互替换。
- 常用于需要根据不同条件选择不同算法的情况,如排序算法和支付方式选择。
模板方法模式
- 定义了一个操作中的算法的骨架,将一些步骤延迟到子类。
- 常用于需要在多个类中实现相同的算法逻辑,但部分步骤需要子类自定义的情况。