组合模式案例

wen java案例 2

本文目录导读:

组合模式案例

  1. 什么是组合模式?
  2. 基础案例:文件系统
  3. 进阶案例:组织结构管理
  4. 安全组合 vs 透明组合
  5. 实战案例:菜单系统
  6. 组合模式总结

我将为你展示组合模式的完整案例,从基础概念到实际应用。

什么是组合模式?

组合模式允许你将对象组合成树形结构来表示“部分-整体”的层次关系,让客户端可以一致地处理单个对象和组合对象。

基础案例:文件系统

1 组件接口

// 抽象组件
public abstract class FileSystemComponent {
    protected String name;
    public FileSystemComponent(String name) {
        this.name = name;
    }
    public abstract void display(int depth);
    public abstract long getSize();
    // 默认实现,叶子节点不需要重写
    public void add(FileSystemComponent component) {
        throw new UnsupportedOperationException();
    }
    public void remove(FileSystemComponent component) {
        throw new UnsupportedOperationException();
    }
    public FileSystemComponent getChild(int index) {
        throw new UnsupportedOperationException();
    }
    protected String getIndent(int depth) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < depth; i++) {
            sb.append("  ");
        }
        return sb.toString();
    }
}

2 叶子节点 - 文件

// 叶子节点:文件
public class File extends FileSystemComponent {
    private long size;  // 文件大小(字节)
    public File(String name, long size) {
        super(name);
        this.size = size;
    }
    @Override
    public void display(int depth) {
        System.out.println(getIndent(depth) + "📄 " + name + " (" + formatSize(size) + ")");
    }
    @Override
    public long getSize() {
        return size;
    }
    private String formatSize(long bytes) {
        if (bytes < 1024) return bytes + " B";
        if (bytes < 1024 * 1024) return String.format("%.1f KB", bytes / 1024.0);
        return String.format("%.1f MB", bytes / (1024.0 * 1024.0));
    }
}

3 复合节点 - 文件夹

// 复合节点:文件夹
public class Folder extends FileSystemComponent {
    private List<FileSystemComponent> children = new ArrayList<>();
    private Date createDate;
    public Folder(String name) {
        super(name);
        this.createDate = new Date();
    }
    @Override
    public void add(FileSystemComponent component) {
        children.add(component);
    }
    @Override
    public void remove(FileSystemComponent component) {
        children.remove(component);
    }
    @Override
    public FileSystemComponent getChild(int index) {
        return children.get(index);
    }
    @Override
    public void display(int depth) {
        System.out.println(getIndent(depth) + "📁 " + name + "/");
        for (FileSystemComponent child : children) {
            child.display(depth + 1);
        }
    }
    @Override
    public long getSize() {
        long totalSize = 0;
        for (FileSystemComponent child : children) {
            totalSize += child.getSize();
        }
        return totalSize;
    }
    public int getChildCount() {
        return children.size();
    }
    public boolean isEmpty() {
        return children.isEmpty();
    }
    public List<FileSystemComponent> getChildren() {
        return children;
    }
}

4 客户端测试代码

public class FileSystemClient {
    public static void main(String[] args) {
        // 创建文件系统结构
        Folder root = new Folder("C:");
        // 创建目录结构
        Folder windows = new Folder("Windows");
        Folder programFiles = new Folder("Program Files");
        Folder users = new Folder("Users");
        // Windows目录内容
        File system32 = new File("system32.dll", 5 * 1024 * 1024);
        File explorer = new File("explorer.exe", 3 * 1024 * 1024);
        windows.add(system32);
        windows.add(explorer);
        // Program Files目录内容
        Folder java = new Folder("Java");
        Folder python = new Folder("Python");
        programFiles.add(java);
        programFiles.add(python);
        // Java目录内容
        File javac = new File("javac.exe", 2 * 1024 * 1024);
        File jar = new File("jar.exe", 1 * 1024 * 1024);
        java.add(javac);
        java.add(jar);
        // Users目录内容
        File report = new File("report.doc", 2048);
        File photo = new File("photo.jpg", 5 * 1024 * 1024);
        users.add(report);
        users.add(photo);
        // 组装树结构
        root.add(windows);
        root.add(programFiles);
        root.add(users);
        // 显示整个文件系统
        System.out.println("=== 文件系统结构 ===");
        root.display(0);
        // 计算总大小
        System.out.println("\n=== 总大小统计 ===");
        System.out.println("C: 总大小: " + formatSize(root.getSize()));
        System.out.println("Windows: " + formatSize(windows.getSize()));
        System.out.println("Program Files: " + formatSize(programFiles.getSize()));
        System.out.println("Users: " + formatSize(users.getSize()));
        // 统一处理叶子节点和复合节点
        System.out.println("\n=== 统一处理演示 ===");
        processComponent(root);
    }
    // 统一处理方法,不需要区分是文件还是文件夹
    private static void processComponent(FileSystemComponent component) {
        System.out.println("处理: " + component.name);
        if (component instanceof Folder) {
            Folder folder = (Folder) component;
            for (FileSystemComponent child : folder.getChildren()) {
                processComponent(child);
            }
        }
    }
    private static String formatSize(long bytes) {
        if (bytes < 1024) return bytes + " B";
        if (bytes < 1024 * 1024) return String.format("%.1f KB", bytes / 1024.0);
        return String.format("%.1f MB", bytes / (1024.0 * 1024.0));
    }
}

进阶案例:组织结构管理

1 组织架构组件

// 抽象组件
public abstract class OrganizationComponent {
    protected String name;
    protected String position;
    public OrganizationComponent(String name, String position) {
        this.name = name;
        this.position = position;
    }
    public abstract void show(int depth);
    public abstract double calculateSalary();
    public abstract int getEmployeeCount();
    // 树形操作方法
    public void add(OrganizationComponent component) {
        throw new UnsupportedOperationException();
    }
    public void remove(OrganizationComponent component) {
        throw new UnsupportedOperationException();
    }
    public OrganizationComponent getChild(int index) {
        throw new UnsupportedOperationException();
    }
    protected String getIndent(int depth) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < depth; i++) {
            sb.append("  ");
        }
        return sb.toString();
    }
}

2 员工(叶子节点)

public class Employee extends OrganizationComponent {
    private double baseSalary;
    private double bonus;
    private String department;
    public Employee(String name, String position, String department, 
                   double baseSalary, double bonus) {
        super(name, position);
        this.department = department;
        this.baseSalary = baseSalary;
        this.bonus = bonus;
    }
    @Override
    public void show(int depth) {
        System.out.println(getIndent(depth) + "👤 " + name + " | " + position + 
                         " | " + department + " | 薪资: " + calculateSalary());
    }
    @Override
    public double calculateSalary() {
        return baseSalary + bonus;
    }
    @Override
    public int getEmployeeCount() {
        return 1;
    }
}

3 部门(复合节点)

public class Department extends OrganizationComponent {
    private List<OrganizationComponent> members = new ArrayList<>();
    private String description;
    private double departmentBonus;
    public Department(String name, String position, String description) {
        super(name, position);
        this.description = description;
        this.departmentBonus = 0;
    }
    @Override
    public void add(OrganizationComponent component) {
        members.add(component);
    }
    @Override
    public void remove(OrganizationComponent component) {
        members.remove(component);
    }
    @Override
    public OrganizationComponent getChild(int index) {
        return members.get(index);
    }
    @Override
    public void show(int depth) {
        System.out.println(getIndent(depth) + "🏢 [" + name + "] " + 
                         position + " - " + description);
        for (OrganizationComponent member : members) {
            member.show(depth + 1);
        }
    }
    @Override
    public double calculateSalary() {
        double total = 0;
        for (OrganizationComponent member : members) {
            total += member.calculateSalary();
        }
        return total;
    }
    @Override
    public int getEmployeeCount() {
        int count = 0;
        for (OrganizationComponent member : members) {
            count += member.getEmployeeCount();
        }
        return count;
    }
    // 部门特有的方法
    public void setDepartmentBonus(double bonus) {
        this.departmentBonus = bonus;
    }
    public String getDescription() {
        return description;
    }
}

4 客户端测试

public class OrganizationClient {
    public static void main(String[] args) {
        // 创建公司结构
        Department company = new Department("TechCorp", "总公司", "科技创新公司");
        // 创建部门
        Department techDept = new Department("技术部", "技术部门", "负责产品研发");
        Department salesDept = new Department("销售部", "销售部门", "负责市场销售");
        Department hrDept = new Department("人力资源部", "人事部门", "负责人员管理");
        // 技术部门员工
        Employee techManager = new Employee("张三", "技术总监", "技术部", 30000, 10000);
        Employee dev1 = new Employee("李四", "高级开发工程师", "技术部", 20000, 5000);
        Employee dev2 = new Employee("王五", "开发工程师", "技术部", 15000, 3000);
        // 创建技术部子团队
        Department frontendTeam = new Department("前端组", "团队", "负责前端开发");
        Department backendTeam = new Department("后端组", "团队", "负责后端开发");
        Employee frontend1 = new Employee("赵六", "前端工程师", "技术部", 12000, 2000);
        Employee frontend2 = new Employee("钱七", "前端工程师", "技术部", 12000, 2000);
        Department frontendArchitect = new Department("架构组", "子团队", "负责架构设计");
        frontendTeam.add(frontend1);
        frontendTeam.add(frontend2);
        frontendArchitect.add(new Employee("孙八", "首席架构师", "技术部", 25000, 8000));
        frontendTeam.add(frontendArchitect);
        backendTeam.add(dev1);
        backendTeam.add(dev2);
        techDept.add(techManager);
        techDept.add(frontendTeam);
        techDept.add(backendTeam);
        // 销售部门
        Employee salesManager = new Employee("周九", "销售总监", "销售部", 15000, 10000);
        Employee sales1 = new Employee("吴十", "销售代表", "销售部", 8000, 2000);
        Employee sales2 = new Employee("郑十一", "销售代表", "销售部", 8000, 2000);
        salesDept.add(salesManager);
        salesDept.add(sales1);
        salesDept.add(sales2);
        // 人力资源部门
        Employee hrManager = new Employee("王十二", "HR总监", "人力资源部", 10000, 3000);
        Employee hr1 = new Employee("李十三", "HR", "人力资源部", 5000, 1000);
        hrDept.add(hrManager);
        hrDept.add(hr1);
        // 组装公司结构
        company.add(techDept);
        company.add(salesDept);
        company.add(hrDept);
        // 显示公司组织架构
        System.out.println("=== 公司组织架构 ===");
        company.show(0);
        // 统计信息
        System.out.println("\n=== 公司统计信息 ===");
        System.out.println("员工总数: " + company.getEmployeeCount() + " 人");
        System.out.println("月度总薪资: ¥" + String.format("%,.2f", company.calculateSalary()));
        // 统计各部门
        System.out.println("\n=== 各部门统计 ===");
        statDepartment(techDept, "技术部");
        statDepartment(salesDept, "销售部");
        statDepartment(hrDept, "人力资源部");
        // 安全删除操作
        System.out.println("\n=== 安全删除测试 ===");
        try {
            Employee emp = new Employee("测试", "测试", "测试", 0, 0);
            emp.show(0);  // 叶子节点可以显示
            emp.add(new Employee("非法", "操作", "测试", 0, 0));  // 会抛出异常
        } catch (UnsupportedOperationException e) {
            System.out.println("正确抛出异常: " + e.getMessage());
        }
    }
    private static void statDepartment(Department dept, String name) {
        System.out.println(name + ": " + dept.getEmployeeCount() + " 人, " + 
                         "薪资: ¥" + String.format("%,.2f", dept.calculateSalary()));
    }
}

安全组合 vs 透明组合

1 安全组合模式(推荐)

// 安全模式:接口只定义叶子节点的方法
public abstract class SafeComponent {
    protected String name;
    public SafeComponent(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public abstract void operation();
}
// 叶子节点
public class Leaf extends SafeComponent {
    public Leaf(String name) {
        super(name);
    }
    @Override
    public void operation() {
        System.out.println("叶子节点: " + name + " 执行操作");
    }
}
// 复合节点 - 有自己的管理模式
public class Composite extends SafeComponent {
    private List<SafeComponent> children = new ArrayList<>();
    public Composite(String name) {
        super(name);
    }
    public void add(SafeComponent component) {
        children.add(component);
    }
    public void remove(SafeComponent component) {
        children.remove(component);
    }
    public SafeComponent getChild(int index) {
        return children.get(index);
    }
    @Override
    public void operation() {
        System.out.println("复合节点: " + name + " 执行操作");
        for (SafeComponent child : children) {
            child.operation();
        }
    }
}

实战案例:菜单系统

// 菜单组件接口
public abstract class MenuComponent {
    protected String name;
    protected String description;
    public MenuComponent(String name, String description) {
        this.name = name;
        this.description = description;
    }
    public abstract void print();
    public abstract double getPrice();
    public void add(MenuComponent menuComponent) {
        throw new UnsupportedOperationException();
    }
    public void remove(MenuComponent menuComponent) {
        throw new UnsupportedOperationException();
    }
    public MenuComponent getChild(int i) {
        throw new UnsupportedOperationException();
    }
    protected String getIndent(int depth) {
        return "  ".repeat(depth);
    }
}
// 菜单项(叶子节点)
public class MenuItem extends MenuComponent {
    private double price;
    private boolean vegetarian;
    public MenuItem(String name, String description, double price, boolean vegetarian) {
        super(name, description);
        this.price = price;
        this.vegetarian = vegetarian;
    }
    @Override
    public void print() {
        System.out.println("  " + name + (vegetarian ? "(素食)" : "") + 
                         " - $" + price);
        System.out.println("    " + description);
    }
    @Override
    public double getPrice() {
        return price;
    }
}
// 菜单(复合节点)
public class Menu extends MenuComponent {
    private List<MenuComponent> menuComponents = new ArrayList<>();
    public Menu(String name, String description) {
        super(name, description);
    }
    @Override
    public void add(MenuComponent menuComponent) {
        menuComponents.add(menuComponent);
    }
    @Override
    public void remove(MenuComponent menuComponent) {
        menuComponents.remove(menuComponent);
    }
    @Override
    public MenuComponent getChild(int i) {
        return menuComponents.get(i);
    }
    @Override
    public void print() {
        System.out.println("\n=== " + name + " ===");
        System.out.println(description);
        System.out.println("---------------------");
        for (MenuComponent component : menuComponents) {
            component.print();
        }
    }
    @Override
    public double getPrice() {
        double total = 0;
        for (MenuComponent component : menuComponents) {
            total += component.getPrice();
        }
        return total;
    }
}
// 测试代码
public class MenuClient {
    public static void main(String[] args) {
        // 创建总菜单
        Menu allMenu = new Menu("全部菜单", "所有菜单选项");
        // 创建分类菜单
        Menu breakfastMenu = new Menu("早餐菜单", "早晨特供");
        Menu lunchMenu = new Menu("午餐菜单", "中午精选");
        Menu dinnerMenu = new Menu("晚餐菜单", "傍晚推荐");
        // 添加早餐菜单项
        breakfastMenu.add(new MenuItem("鸡蛋三明治", "全麦面包配煎蛋和蔬菜", 5.99, false));
        breakfastMenu.add(new MenuItem("燕麦粥", "新鲜燕麦配水果", 3.99, true));
        // 创建午餐子菜单
        Menu mainCourseMenu = new Menu("主菜", "午餐主菜");
        Menu dessertMenu = new Menu("甜点", "美味甜点");
        mainCourseMenu.add(new MenuItem("烤鸡沙拉", "新鲜蔬菜配烤鸡胸肉", 8.99, false));
        mainCourseMenu.add(new MenuItem("素食意面", "番茄酱配意大利面", 6.99, true));
        dessertMenu.add(new MenuItem("提拉米苏", "传统意大利甜点", 4.99, true));
        dessertMenu.add(new MenuItem("草莓蛋糕", "新鲜草莓配奶油蛋糕", 3.99, true));
        lunchMenu.add(mainCourseMenu);
        lunchMenu.add(dessertMenu);
        // 添加晚餐菜单项
        dinnerMenu.add(new MenuItem("牛排套餐", "精选牛肉配时蔬", 19.99, false));
        dinnerMenu.add(new MenuItem("烤鱼", "时令鲜鱼配柠檬汁", 15.99, false));
        // 组装菜单
        allMenu.add(breakfastMenu);
        allMenu.add(lunchMenu);
        allMenu.add(dinnerMenu);
        // 打印菜单
        allMenu.print();
        // 计算总价
        System.out.println("\n=== 总价计算 ===");
        System.out.printf("全部菜单总价: $%.2f%n", allMenu.getPrice());
        System.out.printf("午餐菜单总价: $%.2f%n", lunchMenu.getPrice());
        System.out.printf("早餐菜单总价: $%.2f%n", breakfastMenu.getPrice());
    }
}

组合模式总结

1 模式结构

┌─────────────────────────────────────────────────────┐
│                 Component (抽象组件)                  │
│  - 定义所有对象的通用方法                               │
│  - 提供默认实现                                      │
└─────────────────────────────────────────────────────┘
                    ▲                    ▲
                   /  \                  |
    ┌─────────────┘    └─────────────┐   |
    │                                │   │
┌──────────────┐            ┌────────────────┐
│ Leaf (叶子)   │            │ Composite (复合) │
│ - 没有子节点   │            │ - 包含子节点     │
│ - 实现具体操作 │            │ - 管理子节点     │
└──────────────┘            └────────────────┘

2 优缺点

优点:

  1. 统一接口:客户端可以一致地处理单个对象和组合对象
  2. 简化客户端代码:不需要区分处理叶子节点和复合节点
  3. 易于扩展:可以轻松增加新的组件类型
  4. 天然递归:适合处理树形结构

缺点:

  1. 设计泛化:可能会让设计过于一般化,难以限制组件类型
  2. 性能问题:对树进行遍历时可能会有性能开销
  3. 安全与透明权衡:需要权衡安全组合和透明组合

3 适用场景

  1. 树形结构:文件系统、组织架构、菜单系统
  2. 递归结构:XML/HTML解析、JSON处理
  3. 图形界面:容器组件和独立组件
  4. 数据模型:目录树、分类树

4 实际应用案例

// Java AWT/Swing中的容器层次
// Java标准库中的目录结构
// 工作流引擎中的任务分解
// 电商系统中的商品分类
// 实际项目示例:多级分类
public class CategoryExample {
    public static void main(String[] args) {
        CategoryComponent root = new Category("电子产品");
        CategoryComponent phone = new Category("手机");
        CategoryComponent notebook = new Category("笔记本");
        phone.add(new Product("iPhone 15", 6999));
        phone.add(new Product("华为 Mate 60", 5999));
        CategoryComponent gaming = new Category("游戏本");
        gaming.add(new Product("联想拯救者", 7999));
        notebook.add(gaming);
        root.add(phone);
        root.add(notebook);
        // 显示所有产品
        root.print();
    }
}

组合模式是处理树形结构数据的重要设计模式,在实际开发中应用广泛,掌握了它能够让代码更加简洁、灵活和可扩展。

抱歉,评论功能暂时关闭!