【设计模式】组合模式

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(); // 统一调用显示方法
    }
}
相关推荐
东阳马生架构6 小时前
Sentinel源码—8.限流算法和设计模式总结二
算法·设计模式·sentinel
冰茶_8 小时前
C#中常见的设计模式
java·开发语言·microsoft·设计模式·微软·c#·命令模式
Niuguangshuo8 小时前
Python 设计模式:访问者模式
python·设计模式·访问者模式
不当菜虚困9 小时前
JAVA设计模式——(七)代理模式
java·设计模式·代理模式
RationalDysaniaer10 小时前
Go设计模式-观察者模式
观察者模式·设计模式·golang
千千寰宇11 小时前
[设计模式/Java] 设计模式之解释器模式【27】
数据库·设计模式
麓殇⊙11 小时前
设计模式-- 原型模式详解
设计模式·原型模式
电子科技圈11 小时前
XMOS空间音频——在任何设备上都能提供3D沉浸式空间音频且实现更安全地聆听
经验分享·设计模式·性能优化·计算机外设·音视频
智想天开11 小时前
11.原型模式:思考与解读
设计模式·原型模式
XiaoLeisj12 小时前
【设计模式】深入解析代理模式(委托模式):代理模式思想、静态模式和动态模式定义与区别、静态代理模式代码实现
java·spring boot·后端·spring·设计模式·代理模式·委托模式