复合设计模式

复合设计模式

复合设计模式是一种结构模式,可让您统一处理单个对象和对象的组合。

它允许您构建树状结构(例如,文件系统、UI 层次结构、组织结构),客户端可以使用同一界面处理单个元素和元素组。

它在以下情况下特别有用:
您需要表示部分-整体层次结构。

您希望 以一致的方式对叶节点和复合节点执行作。
您希望避免编写特殊情况逻辑来区分“单个”和“分组”对象。

在设计此类系统时,开发人员通常从 块或类型检查开始,以不同于集合的方式处理单个项目。例如, 在决定执行什么作之前,方法可能必须检查元素是按钮、面板还是容器。if-elserender()

但是,随着结构的复杂性增加,这种方法变得难以扩展,违反了开放/关闭原则,并在客户端代码和结构的内部组合之间引入了紧密耦合。

复合图案通过为所有元素定义一个通用接口来解决这个问题,无论它们是叶子还是复合元素。然后可以以相同的方式处理每个组件——允许客户端像简单对象一样对复杂的结构进行作。

让我们通过一个真实世界的示例,看看如何应用复合模式来建模一个既干净又可扩展的灵活分层系统。

问题:对文件资源管理器进行建模
想象一下,您正在构建一个文件资源管理器应用程序(例如 macOS 上的 Finder 或 Windows 上的文件资源管理器)。系统需要表示:

文件 – 具有名称和大小的简单项目。
文件夹 – 可以保存文件 和其他文件夹(甚至嵌套文件夹)的容器。

您的目标是支持以下作:
getSize()– 返回文件或文件夹的总大小(这是所有内容的总和)。
printStructure()– 打印项目的名称,包括缩进以显示层次结构。
delete()– 删除文件或文件夹及其中的所有内容。

天真的方法
一个简单的解决方案可能涉及两个单独的类: 和 。这是一个简化版本:FileFolder

文件

   class File {private String name;private int size;public int getSize() {return size;}public void printStructure(String indent) {System.out.println(indent + name);}public void delete() {System.out.println("Deleting file: " + name);}
}

文件夹

   import java.util.ArrayList;
import java.util.List;class Folder {private String name;private List<Object> contents = new ArrayList<>();public int getSize() {int total = 0;for (Object item : contents) {if (item instanceof File) {total += ((File) item).getSize();} else if (item instanceof Folder) {total += ((Folder) item).getSize();}}return total;}public void printStructure(String indent) {System.out.println(indent + name + "/");for (Object item : contents) {if (item instanceof File) {((File) item).printStructure(indent + "  ");} else if (item instanceof Folder) {((Folder) item).printStructure(indent + "  ");}}}public void delete() {for (Object item : contents) {if (item instanceof File) {((File) item).delete();} else if (item instanceof Folder) {((Folder) item).delete();}}System.out.println("Deleting folder: " + name);}

这种方法有什么问题?
随着结构变得越来越复杂,该解决方案引入了几个关键问题:

  1. 1. 重复类型检查
    像 、 和 这样的作 需要重复 检查和向下转换——导致逻辑重复和脆弱。getSize()printStructure()delete()instanceof
  2. 2. 没有共享抽象
    和 没有通用接口,这意味着您不能统一对待它们。你不能编写这样的代码:FileFolder
   List<FileSystemItem> items = List.of(file, folder);
for (FileSystemItem item : items) {item.delete();
}
  1. 3. 违反开放/关闭原则
    要添加新的项目类型(例如 , ),您必须修改发生类型检查的每个位置的现有逻辑,这会增加错误和回归的风险。ShortcutCompressedFolder
  2. 4. 缺乏递归优雅
    删除深度嵌套的文件夹或跨多个级别计算大小会变成嵌套条件和递归检查的混乱。

我们真正需要什么
我们需要一个解决方案:

为所有组件引入通用接口(例如,)。FileSystemItem
允许 通过多态性统一处理文件和文件夹。
使文件夹能够包含同一接口的列表,支持任意嵌套。
支持递归作,如 delete 和 getSize,无需类型检查。

使系统易于扩展 — 无需修改现有逻辑即可添加新的项目类型。
这正是复合设计模式所针对的问题。

复合模式
复合设计模式是一种结构设计模式,可让您以统一的方式处理单个对象和对象组。

在复合结构中,层次结构中的每个节点共享相同的接口,无论是叶子(例如 a )还是复合节点(例如 a )。这允许客户端在 两者之间递归一致地执行 、 或 等作。FileFoldergetSize()delete()render()

类图

组件接口(例如: 声明所有具体组件的通用接口)FileSystemItem
叶子(例如): 表示最终对象(无子对象)File
复合(例如): 表示可以容纳子项(包括其他复合)的对象Folder
客户端(例如): 使用共享界面处理组件FileExplorerApp

实现复合
我们将首先为文件系统中的所有项目定义一个通用接口,允许统一处理文件和文件夹。

  1. 1. 定义组件接口
   public interface FileSystemItem {int getSize();void printStructure(String indent);void delete();
}

此接口可确保所有文件系统项(无论是文件还是文件夹)向客户端公开相同的行为。

  1. 2. 创建 Leaf 类 –File
  public class File implements FileSystemItem {private final String name;private final int size;public File(String name, int size) {this.name = name;this.size = size;}@Overridepublic int getSize() {return size;}@Overridepublic void printStructure(String indent) {System.out.println(indent + "- " + name + " (" + size + " KB)");}@Overridepublic void delete() {System.out.println("Deleting file: " + name);}
}

每个 都是叶节点。它不包含任何子项。File

  1. 3. 创建复合类 –Folder
  import java.util.ArrayList;
import java.util.List;public class Folder implements FileSystemItem {private final String name;private final List<FileSystemItem> children = new ArrayList<>();public Folder(String name) {this.name = name;}public void addItem(FileSystemItem item) {children.add(item);}@Overridepublic int getSize() {int total = 0;for (FileSystemItem item : children) {total += item.getSize();}return total;}@Overridepublic void printStructure(String indent) {System.out.println(indent + "+ " + name + "/");for (FileSystemItem item : children) {item.printStructure(indent + "  ");}}@Overridepublic void delete() {for (FileSystemItem item : children) {item.delete();}System.out.println("Deleting folder: " + name);}
}

该 类是复合类。它可以同时包含 和 实例,使其具有递归性和可扩展性。FolderFileFolder

  1. 4. 客户端代码
  public class FileExplorerApp {public static void main(String[] args) {FileSystemItem file1 = new File("readme.txt", 5);FileSystemItem file2 = new File("photo.jpg", 1500);FileSystemItem file3 = new File("data.csv", 300);Folder documents = new Folder("Documents");documents.addItem(file1);documents.addItem(file3);Folder pictures = new Folder("Pictures");pictures.addItem(file2);Folder home = new Folder("Home");home.addItem(documents);home.addItem(pictures);System.out.println("---- File Structure ----");home.printStructure("");System.out.println("\nTotal Size: " + home.getSize() + " KB");System.out.println("\n---- Deleting All ----");home.delete();}
}

输出

---- File Structure ----
+ Home/
+ Documents/- readme.txt (5 KB)- data.csv (300 KB)
+ Pictures/- photo.jpg (1500 KB)Total Size: 1805 KB---- Deleting All ----
Deleting file: readme.txt
Deleting file: data.csv
Deleting folder: Documents
Deleting file: photo.jpg
Deleting folder: Pictures
Deleting folder: Home

使用复合模式,我们按照文件系统的自然工作方式对文件系统进行了建模,将其建模为项目树,其中一些是叶子,另一些是容器。每个作 (, , ) 现在都是模块化的、递归的和可扩展的。getSize()printStructure()delete()

我们通过复合材料取得了什么成就?
统一处理:文件和文件夹共享一个通用界面,允许多态性

干净递归:不,没有铸造——只是委托instanceof

可扩展性:轻松支持深度嵌套结构

可维护性:添加新文件类型(例如,Shortcut、CompressedFolder)很容易

可扩展性:可以通过接口扩展或访问者模式添加新作

其他阅读材料:

https://pan.baidu.com/s/1c1oQItiA7nZxz8Rnl3STpw?pwd=yftc
https://pan.quark.cn/s/dec9e4868381

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用 Prometheus 监控服务器节点:Node Exporter 详解与配置

前言 在上一篇文章中&#xff0c;我们介绍了如何在 CentOS 上部署 Prometheus 并使用 systemd 进行管理。本文将继续深入&#xff0c;讲解如何使用 Prometheus 监控服务器节点&#xff0c;重点介绍 Node Exporter 的作用、安装和配置方法。 Node Exporter 是 Prometheus 生态…

C# 编写一个XmlToDota的转换工具

以下代码可以将Labelme标注的旋转框Xml格式文件转换为Yolo标注格式的txt文件&#xff0c;以便用Yolo OBB训练自己的数据集&#xff1a;using System; using System.Collections.Generic; using System.IO; using System.Xml; using System.Linq; using System.Globalization;na…

[Android] 人体细胞模拟器1.5

[Android] 人体细胞模拟器1.5 链接&#xff1a;https://pan.xunlei.com/s/VOYVUieTpjNVJq-bMys4EEDGA1?pwdm7m6# 省流:这个软件的开发者有点逆天&#xff0c;一个模拟人体器官的软件&#xff0c;细致到有血液报告&#xff0c;还缝合了生理学和病理学&#xff0c;甚至还能做切…

【Linux基础知识系列】第一百一十篇 - 使用Nmap进行网络安全扫描

在网络安全管理中&#xff0c;了解网络中的设备、开放的端口以及运行的服务是至关重要的。Nmap&#xff08;Network Mapper&#xff09;是一个功能强大的开源工具&#xff0c;用于网络发现和安全审计。它可以扫描网络中的设备&#xff0c;识别开放的端口和运行的服务&#xff0…

【Linux仓库】进程的“夺舍”与“飞升”:exec 驱动的应用现代化部署流水线

&#x1f31f; 各位看官好&#xff0c;我是egoist2023&#xff01; &#x1f30d; Linux Linux is not Unix &#xff01; &#x1f680; 今天来学习exec系列的进程程序替换,从"fork"的"克隆"到"exec"的"重生"。 &#x1f44d; 如果觉…

Reachability Query

题目分析 该代码实现了一个动态集合管理系统&#xff0c;支持三种操作&#xff1a;合并集合、切换元素状态、查询集合中是否- 存在活跃元素。核心数据结构为并查集&#xff0c;结合状态标记数组和计数器。关键数据结构与函数 初始化 fa[N]&#xff1a;并查集父节点数组&#xf…

SSL移动接入方案和移动资源发布

一、SSL VPN概述SSL VPN是一种基于SSL/TLS协议的远程安全接入技术&#xff0c;因其广泛兼容Web浏览器&#xff0c;支持“无客户端”部署&#xff0c;具备易于使用和维护的特点。它通过插件系统支持非Web类TCP/UDP应用&#xff0c;并且支持对用户的访问可以做出限制&#xff0c;…

C++STL---count() 统计容器中特定元素出现次数

在 C 标准库中&#xff0c;count 是一个用于统计容器中特定元素出现次数的函数&#xff0c;定义在 <algorithm> 头文件中。它可以快速计算某个值在容器&#xff08;如数组、vector、list 等&#xff09;中出现的次数&#xff0c;避免手动编写循环计数的麻烦。 一、函数原…

Tesla自动驾驶域控制器(AutoPilot HW)的系统化梳理

目前网络上对Tesla自动驾驶硬件&#xff08;AP1-AP4、HW1.0-HW4.0&#xff09;迭代的相关介绍比较混乱&#xff0c;本文这里进行系统化梳理并澄清&#xff0c;并对一些错误进行更正。1、AutoPilot HW迭代图图1 AutoPilot HWMCU迭代图图2 AutoPilot HW 散热设计迭代图&#xff0…

C 语言:第 20 天笔记:typedef(类型重命名规则、应用场景与实战案例)

C语言&#xff1a;第20天笔记 内容提要 构造类型枚举类型typedef综合案例:斗地主预处理 构造类型&#xff1a;枚举类型 使用建议 如果定义不相干的常量&#xff0c;使用宏定义&#xff08;符号常量&#xff09;&#xff1b;如果需要定义一组相关联的常量&#xff08;如月份011、…

在 vue3 和 vue2 中,v-for 和 v-if 可以一起用吗,区别是什么

在 Vue 2 和 Vue 3 中&#xff0c;v-for 和 v-if 可以一起使用&#xff0c;但两者在处理顺序和推荐用法上存在明显区别&#xff0c;主要体现在优先级和最佳实践上&#xff1a; 1. Vue 2 中的 v-for 与 v-if优先级&#xff1a;v-for 的优先级高于 v-if。 这意味着 Vue 会先循环渲…

Linux-进程相关函数

文章目录Linux-进程相关函数父子进程关系父子进程地址空间getpid函数 获取本进程号getppid函数 获取当前进程的进程的父进程号getpgid函数 获取进程组号示例fork函数 创建进程区分父子进程exit函数 进程退出wait函数 等待子进程退出waitpid函数Linux-进程相关函数 每个进程都由…

数据挖掘 6.1 其他降维方法(不是很重要)

6.1 Other dimensionality reduction methods 6.1 其他降维方法 其他降维方法前言问题答案流形3 降维大纲3.1 线性方法3.2 非线性方法3.2.1 流形学习方法&#xff08;Manifold Learning&#xff09;3.2.2 概率方法&#xff08;Probabilistic Approaches&#xff09;3.2.3 拓扑数…

Unity中的特殊文件夹

一.工程路径获取print(Application.dataPath);只用于游戏开发编辑器模式下&#xff0c;游戏发布后此路径就不存在了二.Resources 资源文件夹//路径获取: //一般不获取 //只能使用Resources相关API进行加载 //如果硬要获取 可以用工程路径拼接print(Application.dataPath "…

Seaborn数据可视化实战:Seaborn高级使用与性能优化教程

Seaborn最佳实践与技巧 学习目标 本课程将深入探讨Seaborn库的高级使用技巧&#xff0c;包括性能优化、常见问题解决方法等&#xff0c;旨在帮助学员掌握如何高效地使用Seaborn进行数据可视化&#xff0c;提升图表的美观度和信息传达效率。 相关知识点 Seaborn最佳实践与技巧 学…

分布式系统与单机系统的优劣势对比

近期有遇到一个本地部署的需求&#xff0c;他们希望用主备方案&#xff0c;这就涉及到了备用系统怎么收费的问题。我们是单机系统&#xff0c;其他友商是分布式系统&#xff0c;那20坐席的手拨需求到底是选单机系统好&#xff0c;还是选分布式系统好呢&#xff1f;了解了两者的…

深度学习:从手写数字识别案例认识pytorch框架

目录 一、PyTorch 核心优势与框架定位 二、实战基础&#xff1a;核心库与数据准备 1. 关键库导入与功能说明 2. MNIST 数据集加载与可视化 &#xff08;1&#xff09;数据集下载与封装 &#xff08;2&#xff09;数据集可视化&#xff08;可选&#xff09; 3. DataLoade…

二分|组合|旋转数组

lc1976dijk min_pathpq. min_wlcr187同lc1823.约瑟夫环class Solution { public:int iceBreakingGame(int num, int target) {int x0;for(int i2;i<num;i){x(xtarget)%i;} return x;} };lc2972计算数组中可移除的子数组数量先找最长递增前缀&#xff0c;再结合递增后缀…

【C语言16天强化训练】从基础入门到进阶:Day 10

&#x1f525;个人主页&#xff1a;艾莉丝努力练剑 ❄专栏传送门&#xff1a;《C语言》、《数据结构与算法》、C语言刷题12天IO强训、LeetCode代码强化刷题、洛谷刷题、C/C基础知识知识强化补充、C/C干货分享&学习过程记录 &#x1f349;学习方向&#xff1a;C/C方向学习者…

云计算与云原生技术探索

&#x1f31f; Hello&#xff0c;我是蒋星熠Jaxonic&#xff01; &#x1f308; 在浩瀚无垠的技术宇宙中&#xff0c;我是一名执着的星际旅人&#xff0c;用代码绘制探索的轨迹。 &#x1f680; 每一个算法都是我点燃的推进器&#xff0c;每一行代码都是我航行的星图。 &#x…