【设计模式】组合模式

1.模式简述

想象一个场景,有很多的文件和文件夹,文件夹中还套着很多的子文件夹。你需要统计所有文件的总大小。但是由于这些文件分布在不同的文件夹中,你无法做到一个一个的点开去统计,你想用程序的方式实现,读取所有的文件夹,然后一层一层的查询。当你尝试统计了几个文件夹后你发现还是无法快速实现。这里就需要组合模式帮你解决这个问题。当你统计文件夹中文件的大小时,无需关心文件夹中是否嵌套了子文件夹。直接调用统一的方法。系统将自动递归的计算所有的文件的大小,这就是组合模式的核心:统一处理个体和组合对象。

适用场景

需要使用树结构标识的菜单或者组织架构。

客户端统一处理单个对象和组合对象。

优点

代码简洁,不需要区分对象类型。

新增组件类型容易,符合开闭原则。

缺点

设计过于抽象,会增加开发人员理解成本。

叶子节点需要实现不需要的方法,需要使用异常捕获。

2.示例代码

类图

代码

java 复制代码
// 组件抽象(可以是接口或抽象类)
interface FileSystemComponent {
void showDetails();
}


// 叶子节点:文件
class File implements FileSystemComponent {
private String name;


public File(String name) { this.name = name; }


@Override
public void showDetails() {
System.out.println("File: " + name);
}
}


// 组合节点:文件夹
class Directory implements FileSystemComponent {
private String name;
private List<FileSystemComponent> children = new ArrayList<>();


public Directory(String name) { this.name = name; }


public void add(FileSystemComponent component) {
children.add(component);
}


@Override
public void showDetails() {
System.out.println("Directory: " + name);
for (FileSystemComponent child : children) {
child.showDetails(); // 递归调用子节点的方法
}
}
}


// 客户端调用
public class Client {
public static void main(String[] args) {
File file1 = new File("a.txt");
File file2 = new File("b.jpg");


Directory dir1 = new Directory("Documents");
dir1.add(file1);
dir1.add(file2);


Directory root = new Directory("C:");
root.add(dir1);
root.showDetails(); // 统一调用
}
}

3.实际案例

设计一个多级菜单系统,菜单可以包含子菜单或菜单项。

统一接口:所有对象(组合/叶子)实现同一接口。

递归操作:组合对象内部通过递归处理子节点。

透明性:客户端无需区分组合对象和叶子对象。

代码实现

java 复制代码
// 组件接口
interface MenuComponent {
    void display();
}


// 叶子节点:菜单项
class MenuItem implements MenuComponent {
    private String name;


    public MenuItem(String name) {
        this.name = name;
    }


    @Override
    public void display() {
        System.out.println("菜单项: " + name);
    }
}


// 组合节点:子菜单
class SubMenu implements MenuComponent {
    private String name;
    private List<MenuComponent> children = new ArrayList<>();


    public SubMenu(String name) {
        this.name = name;
    }


    public void add(MenuComponent component) {
        children.add(component);
    }


    @Override
    public void display() {
        System.out.println("子菜单: " + name);
        for (MenuComponent child : children) {
            child.display(); // 递归显示子菜单或菜单项
        }
    }
}


// 客户端使用
public class MenuClient {
    public static void main(String[] args) {
        MenuComponent mainMenu = new SubMenu("主菜单");


        MenuComponent fileMenu = new SubMenu("文件");
        fileMenu.add(new MenuItem("新建"));
        fileMenu.add(new MenuItem("打开"));


        MenuComponent editMenu = new SubMenu("编辑");
        editMenu.add(new MenuItem("复制"));
        editMenu.add(new MenuItem("粘贴"));


        mainMenu.add(fileMenu);
        mainMenu.add(editMenu);


        mainMenu.display(); // 统一调用显示方法
    }
}
相关推荐
Zane199412 小时前
策略模式现在该不该上?一次讲清楚过度设计和设计不足怎么找平衡
设计模式
她说..14 小时前
常见设计模式-模板方法模式
java·spring·设计模式·springboot
xiaofeiyang1501 天前
第六章 · 桥接 — 三支毛笔,画出九种颜色
设计模式
Shadow(⊙o⊙)2 天前
OTOL设计模式 One Thread One Loop
服务器·网络·设计模式
sarasuki2 天前
如何让LLM 能在半夜偷偷打开网易云呢?
人工智能·设计模式·agent
小王师傅662 天前
【设计模式】装饰模式(四):框架源码实战——从 Java I/O 到 Spring 到 MyBatis
java·设计模式
执明wa2 天前
Android RecyclerView 多类型, 多种 Item
android·xml·开发语言·设计模式·android studio
sarasuki2 天前
如何让 Agent 获取更多的能力?插件 / 技能系统
人工智能·设计模式·agent
sarasuki2 天前
如何让 Agent 安全运行你的命令 :命令分级 + Hook + 读写锁
人工智能·设计模式·agent
Zane19943 天前
技术不难,为什么项目却越改越不敢动?聊聊复杂系统开发该怎么想
设计模式