【设计模式】组合模式

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(); // 统一调用显示方法
    }
}
相关推荐
李宥小哥2 小时前
行为型设计模式2
windows·设计模式
Juchecar2 小时前
Java示例:设计模式是如何在实战中“自然生长”出来
java·设计模式
Juchecar2 小时前
超越经典23种设计模式:新模式、反模式与函数式编程
设计模式·云原生·函数式编程
Juchecar2 小时前
设计模式不是Java专属,其他语言的使用方法
java·python·设计模式
_Power_Y3 小时前
Linux&git入门&设计模式(常考点)
linux·git·设计模式
执笔论英雄14 小时前
【大模型训练】加载load_state 中的一些技巧 工厂设计模式
设计模式
gladiator+19 小时前
Java中的设计模式------策略设计模式
java·开发语言·设计模式
在未来等你1 天前
AI Agent设计模式 Day 2:Plan-and-Execute模式:先规划后执行的智能策略
设计模式·llm·react·ai agent·plan-and-execute
在未来等你1 天前
AI Agent设计模式 Day 3:Self-Ask模式:自我提问驱动的推理链
设计模式·llm·react·ai agent·plan-and-execute
xiaodaidai丶1 天前
设计模式之策略模式
设计模式·策略模式