从0开始学PHP面向对象内容之常用设计模式(组合,外观,代理)

在这里插入图片描述

二、结构型设计模式

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

小结

代理模式通过在真实对象和客户端之间增加代理对象,实现了对对象访问的控制、延迟加载、权限校验等功能。在实际开发中,代理模式广泛应用于远程调用、资源管理、权限控制等场景,特别是在需要解耦客户端与真实对象时,是一个非常实用的设计模式。

总结

代理模式与其他模式的对比
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/924375.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux 线程互斥

目录 0.前言 1.相关概念 2.互斥量(mutex) 2.1 代码引入 2.2为什么需要互斥量 2.3互斥量的接口 2.3.1 初始化互斥量 2.3.2 销毁互斥量 2.3.3 互斥量加锁和解锁 2.4改写代码 3.互斥量的封装 4.小结 (图像由AI生成) 0.前言 在多线…

前端实用知识-用express搭建本地服务器

目录 一、为什么会有这篇文章? 二、使用前的准备-如环境、工具 三、如何使用?-express常用知识点 四、代码演示-配合截图,简单易懂 一、为什么会有这篇文章? 在日常前端开发中,我们离不开数据,可能是用…

用nextjs开发时遇到的问题

这几天已经基本把node后端的接口全部写完了,在前端开发时考虑时博客视频类型,考虑了ssr,于是选用了nextJs,用的是nextUi,tailwincss,目前碰到两个比较难受的事情。 1.nextUI个别组件无法在服务器段渲染 目前简单的解决方法&…

【数据结构】二叉树(2)

目录 1. 二叉树的遍历 前序遍历 中序遍历 后序遍历 2. 计算二叉树中的节点个数 3. 计算二叉树中叶子节点个数 4. 计算二叉树的深度 5. 计算二叉树第k层节点个数 6. 二叉树基础练习 7. 二叉树的创建 8. 二叉树的销毁 9. 层序遍历 10. 判断二叉树是否为完全二叉树 1…

比特币与区块链原理解析:矿机挖矿与去中心化的未来

✨✨ 欢迎大家来访Srlua的博文(づ ̄3 ̄)づ╭❤~✨✨ 🌟🌟 欢迎各位亲爱的读者,感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢,在这里我会分享我的知识和经验。&am…

StarRocks-join优化

1、背景 有两个大表,都是6kw级别上下的,通过SR然后包装了一个接口对外提供查询,当前的问题是,这样大的join查询会导致BE直接宕机。并且这个sql很有代表性,我截图如下: 这个表是个单分区,所以直接…

Qt中2D绘制系统

目录 一、Qt绘制系统 1.1Qt绘制基本概念 1.2 绘制代码举例 1.3画家 1.3.1 QPainter的工作原理: 1.3.2 自定义绘制饼状图: 1.4画笔和画刷 1.4.1画笔 1.4.2 画刷填充样式 1.5 反走样和渐变 1.6绘制设备 1.7坐标变换 1.8QPainterPath 1.9绘制文…

基于.NET调用WebService服务

基于.NET调用WebService服务 上一篇文章用java的Spring Boot框架搭建了一个WebService服务端,这篇文章通过.NET进行调用,下文基于Visual Studio 2022 引入WebService服务 项目右键 -> 添加 -> 服务引用 选择WCF Web Service,点击下一…

IIC 随机写+多次写 可以控制写几次

verilog module icc_tx#(parameter SIZE 2 , //用来控制写多少次 比如地址是0000 一个地址只能存放8bit数据 超出指针就会到下一个地址0001parameter CLK_DIV 50_000_000 ,parameter SPEED 100_000 ,parameter LED 50 )( input wire c…

微信小程序+Vant-自定义选择器组件(多选

实现效果 无筛选,如有需要可参照单选组件中的方法.json文件配置"component": true,columns需要处理成含dictLabel和dictValue字段,我是这样处理的: let list arr.map(r > {return {...r,dictValue: r.xxxId,dictLabel: r.xxx…

基于边缘智能网关的机房安全监测应用

随着我国工业互联网的扎实推进,越来越多地区积极建设信息基础设施,以充沛算力支撑产业物联网的可持续发展,数据机房就是其中的典型代表。而且随着机房规模的扩大,对于机房的安全管理难题挑战也日益增加。 面向数据机房安全监测与管…

HarmonyOS 应用跨团队 Debug 协作

文章目录 前言案例背景与问题分析问题背景问题分析工具 方法与代码实现前端模块的优化:日志记录与网络监听日志记录代码示例代码解析实现逻辑实际应用场景 网络状态监听代码示例代码解析实现逻辑实际应用场景 后端模块的优化:接口性能与容错机制接口性能…

《UnityShader 入门精要》更复杂的光照

代码&示例图见:zaizai77/Shader-Learn: 实现一些书里讲到的shader 到了这里就开启了书里的中级篇,之后会讲解 Unity 中的渲染路径,如何计算光照衰减和阴影,如何使用高级纹理和动画等一系列进阶内容 Unity 中的渲染路径 在U…

Ubuntu20.04安装kalibr

文章目录 环境配置安装wxPython下载编译测试报错1问题描述问题分析问题解决 参考 环境配置 Ubuntu20.04,python3.8.10,boost自带的1.71 sudo apt update sudo apt-get install python3-setuptools python3-rosinstall ipython3 libeigen3-dev libboost…

P1198 [JSOI2008] 最大数

P1198 [JSOI2008] 最大数https://www.luogu.com.cn/problem/P1198 牵制芝士:单调队列 思路: 我们的任务是找出一个区间最大值的 因为插入的数与上一次的答案有关 所以它是强制在线的(真无语了) 我们可以在每次插入时整一个叫…

宠物电商对接美团闪购:实现快速配送与用户增值

随着宠物行业的快速发展,宠物电商市场也在不断扩张。消费者的需求不再局限于传统的线上购物模式,越来越多的人开始追求更快捷的配送服务和更优质的购物体验。为了适应这一趋势,许多宠物电商平台开始寻求与本地配送平台合作,以提供…

阿里云oss转发上线-实现不出网钓鱼

本地实现阿里云oss转发上线,全部代码在文末,代码存在冗余 实战环境 被钓鱼机器不出网只可访问内部网络包含集团oss 实战思路 若将我们的shellcode文件上传到集团oss上仍无法上线,那么就利用oss做中转使用本地转发进行上线,先发送…

新型大语言模型的预训练与后训练范式,阿里Qwen

前言:大型语言模型(LLMs)的发展历程可以说是非常长,从早期的GPT模型一路走到了今天这些复杂的、公开权重的大型语言模型。最初,LLM的训练过程只关注预训练,但后来逐步扩展到了包括预训练和后训练在内的完整…

C#结构体排序(数组)

结构体排序(数组) 1 示例1.1 以PointF为例展示效果1.2 运行结果展示 2实际运用2.1 创建结构体2.2 调用示例2.3 运行结果展示 1 示例 1.1 以PointF为例展示效果 private void button1_Click(object sender, EventArgs e) {Random random new Random();…

前端高频面试题-并发请求

面试题中,有一道题经常会出现,咱们下面讲一下思路以及写法写一个方法,传入一个请求地址数组,以及一个并发数量,根据并发数量,一起发送请求。如果一个发送完,那么从数组中拿出来一个接着发送&…