二、结构型设计模式
4、组合模式(Composite)
组合模式(Composite Pattern)是一种结构型设计模式,它将对象组合成树形结构以表示”部分–整体“的层次结构。通过组合模式,客户端可以以一致的方式处理单个对象和对象的组合
组合模式的核心概念
1、组合模式允许客户端同意对待单个对象和对象组合,而不需要分别对待。
2、树形结构是其核心,通过递归调用处理每个对象节点。
3、组合模式适用于需要表示”部分–整体“关系的场景,例如文件系统,组织结构系统等
组成角色
1、组件(Componet):定义所有检点的通用接口,声明默认行为。
2、叶子节点(Leaf):表示树的叶子节点,具体的对象,不能包含子节点。
3、组合节点(Composite):表示树的中间节点,可以包含其他叶子节点或组合节点
4、客户端(Client):使用组件接口来统一操作树形结构中的元素
实现
以下以“文件系统”为例,展示组合模式的实现
1、定义接口:定义所有节点的通用接口
// 组件接口
interface FileSystemComponent {
public function display($indent = 0);
}
2、叶子节点:表示单个文件,不能包含子节点。
// 叶子节点
class File implements FileSystemComponent {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function display($indent = 0) {
echo str_repeat(" ", $indent) . "File: " . $this->name . PHP_EOL;
}
}
3、组合节点:表示文件夹,可以包含文件或子文件夹
// 组合节点
class Directory implements FileSystemComponent {
private $name;
private $children = [];
public function __construct($name) {
$this->name = $name;
}
public function add(FileSystemComponent $component) {
$this->children[] = $component;
}
public function display($indent = 0) {
echo str_repeat(" ", $indent) . "Directory: " . $this->name . PHP_EOL;
foreach ($this->children as $child) {
$child->display($indent + 2);
}
}
}
4、客户端:使用组合模式创建和操作文件系统
// 创建文件和目录
$root = new Directory("root");
$home = new Directory("home");
$root->add($home);
$file1 = new File("file1.txt");
$file2 = new File("file2.txt");
$home->add($file1);
$home->add($file2);
$subDir = new Directory("documents");
$home->add($subDir);
$file3 = new File("resume.docx");
$subDir->add($file3);
// 显示文件系统结构
$root->display();
输出结果
Directory: root
Directory: home
File: file1.txt
File: file2.txt
Directory: documents
File: resume.docx
组合模式的应用场景
1、user_deposit:文件和文件夹的层次结构
2、图形界面(GUI):界面中的窗口,按钮,文本框等组件的层次结构
3、组织架构:公司部门与员工的层次关系。
4、菜单系统:菜单与子菜单的层次结构
扩展
1、添加移除功能:允许动态添加、移除子节点
class Directory implements FileSystemComponent {
public function remove(FileSystemComponent $component) {
$this->children = array_filter($this->children, fn($child) => $child !== $component);
}
}
2、缓存机制:在组合节点中添加缓存,提高查询效率
class Directory {
private $sizeCache;
public function calculateSize() {
if ($this->sizeCache === null) {
$this->sizeCache = array_reduce($this->children, fn($carry, $child) => $carry + $child->calculateSize(), 0);
}
return $this->sizeCache;
}
}
小结
组合模式通过树形结构将单个对象与组合对象统一处理,简化了客户端代码的复杂性,尤其适用于具有层次关系的系统。PHP实现组合模式非常灵活,可以根据实际需求动态调整子节点的添加、移除以及递归行为,极大地提高了代码的扩展性和复用性。
5、外观模式(Facade)
外观模式(Face Pattern)是一种结构型设计模式,通过为子系统中的一组复杂接口提供一个一致的接口,简化了客户端与子系统之间的交互,它隐藏了子系统的复杂性,使得客户端只需要与外观对象交互即可完成操作
外观模式的定义
1、核心思想:为复杂系统提供一个统一的高层接口,使得客户端可以通过该接口访问子系统,而无需直接与子系统交互
2、主要目的:降低系统的复杂性,提高系统的可维护性和易用性
外观模式的组成
1、外观类(Facade):提供一个简单的接口,封装子系统的功能。
2、子系统类(Subsystem):完成系统的实际功能,但客户端无需访问这些类
3、客户端(Client ):通过外观类与子系统交互而不直接依赖子系统的具体实现
实现
以下通过一个“智能家居系统”的例子展示外观模式的实现。
子系统类:包含 灯光,空调等
class Light {
public function on() {
echo "Light is turned on.\n";
}
public function off() {
echo "Light is turned off.\n";
}
}
class AirConditioner {
public function on() {
echo "Air Conditioner is turned on.\n";
}
public function off() {
echo "Air Conditioner is turned off.\n";
}
}
class Television {
public function on() {
echo "Television is turned on.\n";
}
public function off() {
echo "Television is turned off.\n";
}
}
外观类 :使用场景 开始场景(打开),结束场景(关闭)
class SmartHomeFacade {
private $light;
private $airConditioner;
private $television;
public function __construct() {
$this->light = new Light();
$this->airConditioner = new AirConditioner();
$this->television = new Television();
}
public function startDay() {
echo "Starting the day...\n";
$this->light->on();
$this->airConditioner->on();
$this->television->on();
}
public function endDay() {
echo "Ending the day...\n";
$this->television->off();
$this->airConditioner->off();
$this->light->off();
}
}
客户端代码
$smartHome = new SmartHomeFacade();
// 启动智能家居
$smartHome->startDay();
// 结束智能家居
$smartHome->endDay();
运行结果
Starting the day...
Light is turned on.
Air Conditioner is turned on.
Television is turned on.
Ending the day...
Television is turned off.
Air Conditioner is turned off.
Light is turned off.
外观模式的应用场景
1、复杂系统的简化接口:大型系统中,多个子系统需要对外提供功能时,可以通过外观类统一接口,降低复杂度
2、模块解耦:不同模块之间通过外观类交互,而不直接依赖子系统的具体实现。
3、跨层访问:在分层架构中,外观模式可以用作每一层的入口点
小结
外观模式通过隐藏子系统的实现细节,为客户端提供了一个简单易用的接口。
它是“高内聚、低耦合”的典范,可以显著降低客户端与子系统之间的耦合度。
在实际开发中,外观模式常用于大型复杂系统、分层架构中,提供模块化和易用性的统一入口。
6、代理模式(Proxy)
代理模式(Proxy Pattern)是一种结构型设计模式,它为其他对象提供一种代理以控制对这个对象的访问。通过代理模式,客户端可以通过代理对象间接与真实对象交互,从而实现对对象的访问控制、延迟加载、功能增强等需求。
代理模式定义
1、核心思想:代理对象是实际对象的代表,负责对真实对象的访问或操作,同时可以添加额外的功能
2、主要目的:控制访问,性能优化或者为对象提供附加功能。
代理模式组成
1、抽象主题(Subject):定义代理类和真实主题的共同接口
2、真实主题(RealSubject):实现抽象主题的具体功能,是实际被访问的对象
3、代理类(Proxy):实现抽象主题,控制对真实主题的访问
代理模式的类型
1、远程代理(Remote Proxy)
1、定义:为位于不同地址空间的对象提供本地代理。
2、示例:远程调用(RPC)或Web服务代理。
3、场景:通过网络访问远程对象(如RPC、RMI)。
2、虚拟代理(Virtual Proxy
1、定义:在需要时才创建对象(延迟加载)。
2、示例:大图像的延迟加载。
3、场景:需要延迟加载资源时(如大文件或复杂对象)。
3、保护代理(Protection Proxy)
1、定义:控制对对象的访问权限。
2、示例:基于用户权限的访问控制。
3、场景:控制对敏感资源的访问,通常结合权限控制。
4、智能引用代理(Smart Proxy):
1、定义:在访问对象时提供附加功能(如日志记录、统计)。
2、示例:记录对象调用次数
3、场景:在访问真实对象前后,执行附加逻辑(如日志记录、调用统计)。
代理模式的设计要点
1、 共享接口:代理类和真实对象应实现相同的接口或继承自相同的抽象类。
2、透明性:客户端代码无需感知代理类的存在,操作与直接访问真实对象一致。
3、延迟加载:虚拟代理应尽量推迟真实对象的创建,减少资源浪费。
实现
以下通过一个“图像查看器”的例子展示代理模式的实现
抽象主题
interface Image {
public function display();
}
真实主题
class RealImage implements Image {
private $fileName;
public function __construct($fileName) {
$this->fileName = $fileName;
$this->loadFromDisk($fileName);
}
private function loadFromDisk($fileName) {
echo "Loading image from disk: $fileName\n";
}
public function display() {
echo "Displaying image: $this->fileName\n";
}
}
代理类
class ProxyImage implements Image {
private $realImage;
private $fileName;
public function __construct($fileName) {
$this->fileName = $fileName;
}
public function display() {
if ($this->realImage === null) {
$this->realImage = new RealImage($this->fileName);
}
$this->realImage->display();
}
}
客户端代码
$image = new ProxyImage("photo.jpg");
// 第一次调用,会加载图像
$image->display();
// 第二次调用,不会重新加载图像
$image->display();
运行结果:
Loading image from disk: photo.jpg
Displaying image: photo.jpg
Displaying image: photo.jpg
代理模式的扩展
动态代理:使用 PHP 的魔术方法(如 __call)实现通用代理,拦截方法调用。
class DynamicProxy {
private $realObject;
public function __construct($realObject) {
$this->realObject = $realObject;
}
public function __call($name, $arguments) {
echo "Logging: Calling method $name\n";
return call_user_func_array([$this->realObject, $name], $arguments);
}
}
// 示例
$realImage = new RealImage("example.jpg");
$proxy = new DynamicProxy($realImage);
$proxy->display();
运行结果:
Loading image from disk: example.jpg
Logging: Calling method display
Displaying image: example.jpg
小结
代理模式通过在真实对象和客户端之间增加代理对象,实现了对对象访问的控制、延迟加载、权限校验等功能。在实际开发中,代理模式广泛应用于远程调用、资源管理、权限控制等场景,特别是在需要解耦客户端与真实对象时,是一个非常实用的设计模式。
总结
代理模式与其他模式的对比