本文目录导读:

我将为你展示组合模式的完整案例,从基础概念到实际应用。
什么是组合模式?
组合模式允许你将对象组合成树形结构来表示“部分-整体”的层次关系,让客户端可以一致地处理单个对象和组合对象。
基础案例:文件系统
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 优缺点
优点:
- 统一接口:客户端可以一致地处理单个对象和组合对象
- 简化客户端代码:不需要区分处理叶子节点和复合节点
- 易于扩展:可以轻松增加新的组件类型
- 天然递归:适合处理树形结构
缺点:
- 设计泛化:可能会让设计过于一般化,难以限制组件类型
- 性能问题:对树进行遍历时可能会有性能开销
- 安全与透明权衡:需要权衡安全组合和透明组合
3 适用场景
- 树形结构:文件系统、组织架构、菜单系统
- 递归结构:XML/HTML解析、JSON处理
- 图形界面:容器组件和独立组件
- 数据模型:目录树、分类树
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();
}
}
组合模式是处理树形结构数据的重要设计模式,在实际开发中应用广泛,掌握了它能够让代码更加简洁、灵活和可扩展。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播