复合设计模式
复合设计模式是一种结构模式,可让您统一处理单个对象和对象的组合。
它允许您构建树状结构(例如,文件系统、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);
}
这种方法有什么问题?
随着结构变得越来越复杂,该解决方案引入了几个关键问题:
-
- 重复类型检查
像 、 和 这样的作 需要重复 检查和向下转换------导致逻辑重复和脆弱。getSize()printStructure()delete()instanceof
- 重复类型检查
-
-
没有共享抽象
和 没有通用接口,这意味着您不能统一对待它们。你不能编写这样的代码:FileFolderList<FileSystemItem> items = List.of(file, folder);
for (FileSystemItem item : items) {
item.delete();
}
-
-
- 违反开放/关闭原则
要添加新的项目类型(例如 , ),您必须修改发生类型检查的每个位置的现有逻辑,这会增加错误和回归的风险。ShortcutCompressedFolder
- 违反开放/关闭原则
-
- 缺乏递归优雅
删除深度嵌套的文件夹或跨多个级别计算大小会变成嵌套条件和递归检查的混乱。
- 缺乏递归优雅
我们真正需要什么
我们需要一个解决方案:
为所有组件引入通用接口(例如,)。FileSystemItem
允许 通过多态性统一处理文件和文件夹。
使文件夹能够包含同一接口的列表,支持任意嵌套。
支持递归作,如 delete 和 getSize,无需类型检查。
使系统易于扩展 --- 无需修改现有逻辑即可添加新的项目类型。
这正是复合设计模式所针对的问题。
复合模式
复合设计模式是一种结构设计模式,可让您以统一的方式处理单个对象和对象组。
在复合结构中,层次结构中的每个节点共享相同的接口,无论是叶子(例如 a )还是复合节点(例如 a )。这允许客户端在 两者之间递归一致地执行 、 或 等作。FileFoldergetSize()delete()render()
类图

组件接口(例如: 声明所有具体组件的通用接口)FileSystemItem
叶子(例如): 表示最终对象(无子对象)File
复合(例如): 表示可以容纳子项(包括其他复合)的对象Folder
客户端(例如): 使用共享界面处理组件FileExplorerApp
实现复合
我们将首先为文件系统中的所有项目定义一个通用接口,允许统一处理文件和文件夹。
-
-
定义组件接口
public interface FileSystemItem {
int getSize();
void printStructure(String indent);
void delete();
}
-
此接口可确保所有文件系统项(无论是文件还是文件夹)向客户端公开相同的行为。
-
-
创建 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;
}@Override
public int getSize() {
return size;
}@Override
public void printStructure(String indent) {
System.out.println(indent + "- " + name + " (" + size + " KB)");
}@Override
public void delete() {
System.out.println("Deleting file: " + name);
}
}
-
每个 都是叶节点。它不包含任何子项。File
-
-
创建复合类 --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); } @Override public int getSize() { int total = 0; for (FileSystemItem item : children) { total += item.getSize(); } return total; } @Override public void printStructure(String indent) { System.out.println(indent + "+ " + name + "/"); for (FileSystemItem item : children) { item.printStructure(indent + " "); } } @Override public void delete() { for (FileSystemItem item : children) { item.delete(); } System.out.println("Deleting folder: " + name); }
}
-
该 类是复合类。它可以同时包含 和 实例,使其具有递归性和可扩展性。FolderFileFolder
-
-
客户端代码
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