文章目录
-
- 什么是组合模式?
- 核心思想
- 生活中的组合模式
- 模式结构
- 基础示例:文件系统
-
- [1. 组件接口](#1. 组件接口)
- [2. 叶子节点 - 文件](#2. 叶子节点 - 文件)
- [3. 容器节点 - 文件夹](#3. 容器节点 - 文件夹)
- [4. 客户端使用](#4. 客户端使用)
- 完整示例:组织架构系统
-
- [1. 组织组件接口](#1. 组织组件接口)
- [2. 叶子节点 - 员工](#2. 叶子节点 - 员工)
- [3. 容器节点 - 部门](#3. 容器节点 - 部门)
- [4. 组织架构客户端](#4. 组织架构客户端)
- 实际应用示例:GUI组件系统
- 组合模式的优点
-
- [1. 统一处理](#1. 统一处理)
- [2. 简化客户端代码](#2. 简化客户端代码)
- [3. 容易扩展](#3. 容易扩展)
- 组合模式的缺点
-
- [1. 设计较复杂](#1. 设计较复杂)
- [2. 类型安全检查](#2. 类型安全检查)
- 适用场景
- 最佳实践
-
- [1. 合理设计组件接口](#1. 合理设计组件接口)
- [2. 考虑透明性和安全性](#2. 考虑透明性和安全性)
- [3. 实现迭代器支持](#3. 实现迭代器支持)
- [组合模式 vs 其他模式](#组合模式 vs 其他模式)
- 总结
什么是组合模式?
组合模式(Composite Pattern)是一种结构型设计模式,它允许你将对象组合成树形结构来表示"部分-整体"的层次结构。组合模式使得客户端对单个对象和组合对象的使用具有一致性。
核心思想
组合模式的核心思想是:用树形结构来表示"部分-整体"的层次结构,让客户端可以统一地处理单个对象和对象组合。
生活中的组合模式
想象一下公司的组织架构:
- 公司有各个部门
- 部门下面有团队
- 团队里面有员工
- 无论是单个员工还是整个部门,都可以执行"工作"这个操作
这就是组合模式的思想!
模式结构
组合模式包含三个核心角色:
- 组件(Component):定义叶子和容器的共同接口
- 叶子(Leaf):表示树中的叶子节点,没有子节点
- 容器(Composite):表示树中的容器节点,有子节点
基础示例:文件系统
1. 组件接口
java
/**
* 文件系统组件接口 - 组件
* 定义文件和文件夹的共同操作
*/
public interface FileSystemComponent {
/**
* 显示组件信息
*/
void display();
/**
* 获取组件名称
*/
String getName();
/**
* 获取组件大小
*/
long getSize();
/**
* 获取组件类型
*/
String getType();
/**
* 是否是文件
*/
boolean isFile();
/**
* 是否是文件夹
*/
boolean isDirectory();
/**
* 获取完整路径
*/
default String getFullPath() {
return getName();
}
}
2. 叶子节点 - 文件
java
/**
* 文件类 - 叶子节点
* 表示文件系统中的文件
*/
public class File implements FileSystemComponent {
private final String name;
private final long size;
private final String extension;
public File(String name, long size) {
this.name = name;
this.size = size;
this.extension = extractExtension(name);
System.out.println("📄 创建文件: " + name);
}
@Override
public void display() {
System.out.printf("📄 %s (%s) - %d bytes%n", name, extension, size);
}
@Override
public String getName() {
return name;
}
@Override
public long getSize() {
return size;
}
@Override
public String getType() {
return "文件";
}
@Override
public boolean isFile() {
return true;
}
@Override
public boolean isDirectory() {
return false;
}
/**
* 获取文件扩展名
*/
private String extractExtension(String filename) {
int lastDot = filename.lastIndexOf('.');
return lastDot > 0 ? filename.substring(lastDot + 1) : "无";
}
/**
* 获取文件扩展名
*/
public String getExtension() {
return extension;
}
@Override
public String toString() {
return String.format("File{name='%s', size=%d, extension='%s'}",
name, size, extension);
}
}
3. 容器节点 - 文件夹
java
import java.util.ArrayList;
import java.util.List;
/**
* 文件夹类 - 容器节点
* 表示文件系统中的文件夹,可以包含文件和其他文件夹
*/
public class Directory implements FileSystemComponent {
private final String name;
private final List<FileSystemComponent> children;
private Directory parent;
public Directory(String name) {
this.name = name;
this.children = new ArrayList<>();
this.parent = null;
System.out.println("📁 创建文件夹: " + name);
}
@Override
public void display() {
System.out.printf("📁 %s/ [%d 个项目]%n", name, children.size());
// 递归显示所有子组件
for (FileSystemComponent child : children) {
System.out.print(" ");
child.display();
}
}
/**
* 添加子组件
*/
public void addComponent(FileSystemComponent component) {
if (component instanceof Directory) {
((Directory) component).setParent(this);
}
children.add(component);
System.out.printf("➕ 添加 %s 到 %s%n", component.getName(), name);
}
/**
* 移除子组件
*/
public void removeComponent(FileSystemComponent component) {
children.remove(component);
System.out.printf("➖ 从 %s 移除 %s%n", name, component.getName());
}
/**
* 获取子组件
*/
public List<FileSystemComponent> getChildren() {
return new ArrayList<>(children);
}
/**
* 根据名称查找组件
*/
public FileSystemComponent findComponent(String name) {
for (FileSystemComponent child : children) {
if (child.getName().equals(name)) {
return child;
}
if (child instanceof Directory) {
FileSystemComponent found = ((Directory) child).findComponent(name);
if (found != null) {
return found;
}
}
}
return null;
}
/**
* 获取文件夹大小(递归计算所有子组件大小)
*/
@Override
public long getSize() {
long totalSize = 0;
for (FileSystemComponent child : children) {
totalSize += child.getSize();
}
return totalSize;
}
@Override
public String getName() {
return name;
}
@Override
public String getType() {
return "文件夹";
}
@Override
public boolean isFile() {
return false;
}
@Override
public boolean isDirectory() {
return true;
}
@Override
public String getFullPath() {
if (parent != null) {
return parent.getFullPath() + "/" + name;
}
return name;
}
/**
* 设置父文件夹
*/
public void setParent(Directory parent) {
this.parent = parent;
}
/**
* 获取父文件夹
*/
public Directory getParent() {
return parent;
}
/**
* 获取文件夹深度
*/
public int getDepth() {
if (parent == null) {
return 0;
}
return parent.getDepth() + 1;
}
@Override
public String toString() {
return String.format("Directory{name='%s', children=%d, size=%d}",
name, children.size(), getSize());
}
}
4. 客户端使用
java
/**
* 文件系统客户端
*/
public class FileSystemClient {
public static void main(String[] args) {
System.out.println("=== 组合模式演示 - 文件系统 ===\n");
// 创建文件系统结构
Directory root = createFileSystem();
// 演示1:显示整个文件系统
demonstrateDisplay(root);
// 演示2:计算大小
demonstrateSizeCalculation(root);
// 演示3:搜索文件
demonstrateSearch(root);
// 演示4:统一处理
demonstrateUniformTreatment();
}
/**
* 创建示例文件系统
*/
private static Directory createFileSystem() {
System.out.println("🛠️ 创建文件系统结构...");
// 根目录
Directory root = new Directory("根目录");
// 文档文件夹
Directory documents = new Directory("文档");
documents.addComponent(new File("简历.pdf", 2048));
documents.addComponent(new File("报告.docx", 4096));
// 图片文件夹
Directory pictures = new Directory("图片");
pictures.addComponent(new File("照片1.jpg", 1024000));
pictures.addComponent(new File("照片2.png", 2048000));
// 在图片文件夹中创建子文件夹
Directory vacation = new Directory("假期");
vacation.addComponent(new File("海滩.jpg", 3072000));
vacation.addComponent(new File("山峰.png", 4096000));
pictures.addComponent(vacation);
// 程序文件夹
Directory programs = new Directory("程序");
programs.addComponent(new File("app.exe", 5120000));
programs.addComponent(new File("setup.msi", 10240000));
// 将文件夹添加到根目录
root.addComponent(documents);
root.addComponent(pictures);
root.addComponent(programs);
root.addComponent(new File("说明.txt", 1024));
System.out.println("✅ 文件系统创建完成\n");
return root;
}
/**
* 演示显示功能
*/
private static void demonstrateDisplay(Directory root) {
System.out.println("1. 文件系统结构显示:");
System.out.println("=" .repeat(50));
root.display();
}
/**
* 演示大小计算
*/
private static void demonstrateSizeCalculation(Directory root) {
System.out.println("\n2. 文件大小计算:");
System.out.println("-".repeat(40));
// 计算整个文件系统大小
System.out.printf("📊 整个文件系统大小: %,.0f KB%n", root.getSize() / 1024.0);
// 计算各个文件夹大小
for (FileSystemComponent child : root.getChildren()) {
System.out.printf("📁 %s 大小: %,.0f KB%n",
child.getName(), child.getSize() / 1024.0);
}
}
/**
* 演示搜索功能
*/
private static void demonstrateSearch(Directory root) {
System.out.println("\n3. 文件搜索演示:");
System.out.println("-".repeat(40));
// 搜索文件
String[] searchTargets = {"简历.pdf", "海滩.jpg", "不存在的文件.xyz"};
for (String target : searchTargets) {
FileSystemComponent found = root.findComponent(target);
if (found != null) {
System.out.printf("✅ 找到文件: %s (%s)%n",
found.getName(), found.getFullPath());
} else {
System.out.printf("❌ 未找到文件: %s%n", target);
}
}
}
/**
* 演示统一处理
*/
private static void demonstrateUniformTreatment() {
System.out.println("\n4. 统一处理演示:");
System.out.println("-".repeat(40));
// 创建混合列表(文件和文件夹)
List<FileSystemComponent> components = new ArrayList<>();
components.add(new File("单个文件.txt", 512));
components.add(new Directory("单个文件夹"));
// 统一处理 - 客户端不需要知道具体类型
System.out.println("统一处理文件和文件夹:");
for (FileSystemComponent component : components) {
System.out.printf("🔹 %s: %s (大小: %d bytes)%n",
component.getType(), component.getName(), component.getSize());
}
}
}
完整示例:组织架构系统
让我们通过一个更复杂的组织架构系统来深入理解组合模式。
1. 组织组件接口
java
import java.util.List;
/**
* 组织组件接口 - 组件
* 定义员工和部门的共同操作
*/
public interface OrganizationComponent {
/**
* 显示组件信息
*/
void display();
/**
* 获取名称
*/
String getName();
/**
* 获取职责描述
*/
String getDescription();
/**
* 计算薪资成本
*/
double calculateCost();
/**
* 获取成员数量
*/
int getMemberCount();
/**
* 添加子组件(部门专用)
*/
default void addComponent(OrganizationComponent component) {
throw new UnsupportedOperationException("不支持添加操作");
}
/**
* 移除子组件(部门专用)
*/
default void removeComponent(OrganizationComponent component) {
throw new UnsupportedOperationException("不支持移除操作");
}
/**
* 获取子组件(部门专用)
*/
default List<OrganizationComponent> getChildren() {
throw new UnsupportedOperationException("不支持获取子组件操作");
}
/**
* 是否是员工
*/
boolean isEmployee();
/**
* 是否是部门
*/
boolean isDepartment();
}
2. 叶子节点 - 员工
java
/**
* 员工类 - 叶子节点
* 表示组织中的员工
*/
public class Employee implements OrganizationComponent {
private final String name;
private final String position;
private final double salary;
private final String department;
public Employee(String name, String position, double salary, String department) {
this.name = name;
this.position = position;
this.salary = salary;
this.department = department;
System.out.printf("👤 创建员工: %s (%s)%n", name, position);
}
@Override
public void display() {
System.out.printf("👤 %s - %s [部门: %s, 薪资: ¥%,.0f]%n",
name, position, department, salary);
}
@Override
public String getName() {
return name;
}
@Override
public String getDescription() {
return String.format("员工 %s - %s", name, position);
}
@Override
public double calculateCost() {
return salary;
}
@Override
public int getMemberCount() {
return 1; // 单个员工就是1个成员
}
@Override
public boolean isEmployee() {
return true;
}
@Override
public boolean isDepartment() {
return false;
}
// Getter方法
public String getPosition() {
return position;
}
public double getSalary() {
return salary;
}
public String getDepartment() {
return department;
}
/**
* 员工工作
*/
public void work() {
System.out.printf("💼 %s 正在工作...%n", name);
}
@Override
public String toString() {
return String.format("Employee{name='%s', position='%s', salary=%.0f}",
name, position, salary);
}
}
3. 容器节点 - 部门
java
import java.util.ArrayList;
import java.util.List;
/**
* 部门类 - 容器节点
* 表示组织中的部门,可以包含员工和其他部门
*/
public class Department implements OrganizationComponent {
private final String name;
private final String description;
private final List<OrganizationComponent> children;
private Department parent;
public Department(String name, String description) {
this.name = name;
this.description = description;
this.children = new ArrayList<>();
this.parent = null;
System.out.printf("🏢 创建部门: %s%n", name);
}
@Override
public void display() {
System.out.printf("🏢 %s - %s [成员: %d 人, 成本: ¥%,.0f]%n",
name, description, getMemberCount(), calculateCost());
// 递归显示所有子组件
for (OrganizationComponent child : children) {
System.out.print(" ");
child.display();
}
}
@Override
public void addComponent(OrganizationComponent component) {
if (component instanceof Department) {
((Department) component).setParent(this);
}
children.add(component);
System.out.printf("➕ 添加 %s 到 %s%n", component.getName(), name);
}
@Override
public void removeComponent(OrganizationComponent component) {
children.remove(component);
System.out.printf("➖ 从 %s 移除 %s%n", name, component.getName());
}
@Override
public List<OrganizationComponent> getChildren() {
return new ArrayList<>(children);
}
@Override
public String getName() {
return name;
}
@Override
public String getDescription() {
return description;
}
/**
* 计算部门总成本(递归计算所有子组件成本)
*/
@Override
public double calculateCost() {
double totalCost = 0;
for (OrganizationComponent child : children) {
totalCost += child.calculateCost();
}
return totalCost;
}
/**
* 获取部门总人数(递归计算所有子组件人数)
*/
@Override
public int getMemberCount() {
int totalCount = 0;
for (OrganizationComponent child : children) {
totalCount += child.getMemberCount();
}
return totalCount;
}
@Override
public boolean isEmployee() {
return false;
}
@Override
public boolean isDepartment() {
return true;
}
/**
* 设置父部门
*/
public void setParent(Department parent) {
this.parent = parent;
}
/**
* 获取父部门
*/
public Department getParent() {
return parent;
}
/**
* 根据名称查找组件
*/
public OrganizationComponent findComponent(String name) {
for (OrganizationComponent child : children) {
if (child.getName().equals(name)) {
return child;
}
if (child instanceof Department) {
OrganizationComponent found = ((Department) child).findComponent(name);
if (found != null) {
return found;
}
}
}
return null;
}
/**
* 获取部门层级
*/
public int getLevel() {
if (parent == null) {
return 0;
}
return parent.getLevel() + 1;
}
/**
* 获取所有员工
*/
public List<Employee> getAllEmployees() {
List<Employee> employees = new ArrayList<>();
for (OrganizationComponent child : children) {
if (child instanceof Employee) {
employees.add((Employee) child);
} else if (child instanceof Department) {
employees.addAll(((Department) child).getAllEmployees());
}
}
return employees;
}
/**
* 部门运营
*/
public void operate() {
System.out.printf("🏢 %s 部门正在运营...%n", name);
for (OrganizationComponent child : children) {
if (child instanceof Employee) {
((Employee) child).work();
} else if (child instanceof Department) {
((Department) child).operate();
}
}
}
@Override
public String toString() {
return String.format("Department{name='%s', members=%d, cost=%.0f}",
name, getMemberCount(), calculateCost());
}
}
4. 组织架构客户端
java
/**
* 组织架构客户端
*/
public class OrganizationClient {
public static void main(String[] args) {
System.out.println("=== 组合模式演示 - 组织架构系统 ===\n");
// 创建公司组织架构
Department company = createCompanyStructure();
// 演示1:显示组织架构
demonstrateOrganizationDisplay(company);
// 演示2:成本计算
demonstrateCostCalculation(company);
// 演示3:统一操作
demonstrateUniformOperations(company);
// 演示4:高级功能
demonstrateAdvancedFeatures(company);
}
/**
* 创建公司组织架构
*/
private static Department createCompanyStructure() {
System.out.println("🏗️ 构建公司组织架构...");
// 创建公司
Department company = new Department("科技有限公司", "总部");
// 技术部门
Department techDepartment = new Department("技术部", "产品研发");
techDepartment.addComponent(new Employee("张三", "Java工程师", 25000, "技术部"));
techDepartment.addComponent(new Employee("李四", "前端工程师", 22000, "技术部"));
techDepartment.addComponent(new Employee("王五", "架构师", 35000, "技术部"));
// 后端开发组
Department backendTeam = new Department("后端开发组", "服务器端开发");
backendTeam.addComponent(new Employee("赵六", "高级Java工程师", 28000, "后端开发组"));
backendTeam.addComponent(new Employee("钱七", "Python工程师", 26000, "后端开发组"));
techDepartment.addComponent(backendTeam);
// 市场部门
Department marketingDepartment = new Department("市场部", "市场推广和销售");
marketingDepartment.addComponent(new Employee("孙八", "市场经理", 30000, "市场部"));
marketingDepartment.addComponent(new Employee("周九", "销售代表", 18000, "市场部"));
marketingDepartment.addComponent(new Employee("吴十", "市场专员", 15000, "市场部"));
// 人事部门
Department hrDepartment = new Department("人事部", "人力资源管理和招聘");
hrDepartment.addComponent(new Employee("郑十一", "HR经理", 28000, "人事部"));
hrDepartment.addComponent(new Employee("王十二", "招聘专员", 16000, "人事部"));
// 构建组织架构
company.addComponent(techDepartment);
company.addComponent(marketingDepartment);
company.addComponent(hrDepartment);
System.out.println("✅ 公司组织架构构建完成\n");
return company;
}
/**
* 演示组织架构显示
*/
private static void demonstrateOrganizationDisplay(Department company) {
System.out.println("1. 公司组织架构:");
System.out.println("=" .repeat(60));
company.display();
}
/**
* 演示成本计算
*/
private static void demonstrateCostCalculation(Department company) {
System.out.println("\n2. 组织成本分析:");
System.out.println("-".repeat(40));
System.out.printf("💰 公司总薪资成本: ¥%,.0f/月%n", company.calculateCost());
System.out.printf("👥 公司总人数: %d 人%n", company.getMemberCount());
System.out.printf("📊 人均成本: ¥%,.0f/月%n",
company.calculateCost() / company.getMemberCount());
// 各部门成本分析
System.out.println("\n📈 各部门成本分析:");
for (OrganizationComponent dept : company.getChildren()) {
System.out.printf("🏢 %s: ¥%,.0f/月 (%d 人)%n",
dept.getName(), dept.calculateCost(), dept.getMemberCount());
}
}
/**
* 演示统一操作
*/
private static void demonstrateUniformOperations(Department company) {
System.out.println("\n3. 统一操作演示:");
System.out.println("-".repeat(40));
// 创建混合列表(员工和部门)
List<OrganizationComponent> components = new ArrayList<>();
components.add(new Employee("临时员工", "实习生", 8000, "临时"));
components.add(new Department("临时部门", "项目组"));
System.out.println("统一处理员工和部门:");
for (OrganizationComponent component : components) {
System.out.printf("🔹 %s: %s (成本: ¥%,.0f, 成员: %d)%n",
component.isEmployee() ? "员工" : "部门",
component.getName(),
component.calculateCost(),
component.getMemberCount());
}
System.out.println("\n🏢 公司运营模拟:");
company.operate();
}
/**
* 演示高级功能
*/
private static void demonstrateAdvancedFeatures(Department company) {
System.out.println("\n4. 高级功能演示:");
System.out.println("-".repeat(40));
// 搜索功能
System.out.println("🔍 员工搜索:");
String[] searchNames = {"张三", "赵六", "不存在的员工"};
for (String name : searchNames) {
OrganizationComponent found = company.findComponent(name);
if (found != null) {
System.out.printf("✅ 找到: %s%n", found.getDescription());
} else {
System.out.printf("❌ 未找到: %s%n", name);
}
}
// 获取所有员工
System.out.println("\n📋 公司所有员工列表:");
List<Employee> allEmployees = company.getAllEmployees();
for (Employee employee : allEmployees) {
System.out.printf("👤 %s - %s (¥%,.0f)%n",
employee.getName(), employee.getPosition(), employee.getSalary());
}
// 动态调整组织架构
System.out.println("\n🔄 动态调整组织架构:");
Department newTeam = new Department("新产品组", "创新产品研发");
newTeam.addComponent(new Employee("新员工", "产品经理", 32000, "新产品组"));
company.addComponent(newTeam);
System.out.printf("调整后总人数: %d 人%n", company.getMemberCount());
System.out.printf("调整后总成本: ¥%,.0f/月%n", company.calculateCost());
}
}
实际应用示例:GUI组件系统
java
import java.util.ArrayList;
import java.util.List;
/**
* GUI组件系统 - 组合模式实际应用
*/
public class GUIComponentSystem {
public static void main(String[] args) {
System.out.println("=== 组合模式演示 - GUI组件系统 ===\n");
// 创建GUI界面
Window mainWindow = createGUI();
// 显示GUI结构
mainWindow.display();
// 演示渲染操作
System.out.println("\n🎨 渲染GUI界面:");
mainWindow.render();
}
private static Window createGUI() {
System.out.println("🖼️ 创建GUI界面...");
// 创建主窗口
Window mainWindow = new Window("主窗口", 800, 600);
// 创建工具栏
Panel toolbar = new Panel("工具栏");
toolbar.addComponent(new Button("文件"));
toolbar.addComponent(new Button("编辑"));
toolbar.addComponent(new Button("视图"));
// 创建侧边栏
Panel sidebar = new Panel("侧边栏");
sidebar.addComponent(new Button("导航"));
sidebar.addComponent(new Button("书签"));
sidebar.addComponent(new Button("历史"));
// 创建内容区域
Panel contentArea = new Panel("内容区域");
contentArea.addComponent(new TextField("搜索框", 200));
// 创建嵌套面板
Panel settingsPanel = new Panel("设置面板");
settingsPanel.addComponent(new Checkbox("自动保存"));
settingsPanel.addComponent(new Checkbox("显示行号"));
contentArea.addComponent(settingsPanel);
// 构建界面
mainWindow.addComponent(toolbar);
mainWindow.addComponent(sidebar);
mainWindow.addComponent(contentArea);
return mainWindow;
}
}
/**
* GUI组件接口
*/
interface GUIComponent {
void display();
void render();
String getName();
}
/**
* 窗口 - 容器组件
*/
class Window implements GUIComponent {
private String name;
private int width;
private int height;
private List<GUIComponent> children;
public Window(String name, int width, int height) {
this.name = name;
this.width = width;
this.height = height;
this.children = new ArrayList<>();
}
public void addComponent(GUIComponent component) {
children.add(component);
}
@Override
public void display() {
System.out.printf("🪟 %s [%dx%d]%n", name, width, height);
for (GUIComponent child : children) {
System.out.print(" ");
child.display();
}
}
@Override
public void render() {
System.out.printf("🎨 渲染窗口: %s%n", name);
for (GUIComponent child : children) {
child.render();
}
}
@Override
public String getName() {
return name;
}
}
/**
* 面板 - 容器组件
*/
class Panel implements GUIComponent {
private String name;
private List<GUIComponent> children;
public Panel(String name) {
this.name = name;
this.children = new ArrayList<>();
}
public void addComponent(GUIComponent component) {
children.add(component);
}
@Override
public void display() {
System.out.printf("📦 面板: %s%n", name);
for (GUIComponent child : children) {
System.out.print(" ");
child.display();
}
}
@Override
public void render() {
System.out.printf("🎨 渲染面板: %s%n", name);
for (GUIComponent child : children) {
child.render();
}
}
@Override
public String getName() {
return name;
}
}
/**
* 按钮 - 叶子组件
*/
class Button implements GUIComponent {
private String label;
public Button(String label) {
this.label = label;
}
@Override
public void display() {
System.out.printf("🔘 按钮: %s%n", label);
}
@Override
public void render() {
System.out.printf("🎨 渲染按钮: %s%n", label);
}
@Override
public String getName() {
return "按钮-" + label;
}
}
/**
* 文本框 - 叶子组件
*/
class TextField implements GUIComponent {
private String placeholder;
private int width;
public TextField(String placeholder, int width) {
this.placeholder = placeholder;
this.width = width;
}
@Override
public void display() {
System.out.printf("📝 文本框: %s [宽度:%d]%n", placeholder, width);
}
@Override
public void render() {
System.out.printf("🎨 渲染文本框: %s%n", placeholder);
}
@Override
public String getName() {
return "文本框-" + placeholder;
}
}
/**
* 复选框 - 叶子组件
*/
class Checkbox implements GUIComponent {
private String label;
public Checkbox(String label) {
this.label = label;
}
@Override
public void display() {
System.out.printf("☑️ 复选框: %s%n", label);
}
@Override
public void render() {
System.out.printf("🎨 渲染复选框: %s%n", label);
}
@Override
public String getName() {
return "复选框-" + label;
}
}
组合模式的优点
1. 统一处理
java
// 客户端可以统一处理单个对象和对象组合
// 不需要关心具体是叶子节点还是容器节点
2. 简化客户端代码
java
// 客户端代码变得简单清晰
// 不需要写很多条件判断
3. 容易扩展
java
// 可以很容易地添加新的组件类型
// 符合开闭原则
组合模式的缺点
1. 设计较复杂
java
// 需要正确设计组件接口
// 要处理叶子节点和容器节点的差异
2. 类型安全检查
java
// 在运行时才能发现某些操作不支持
// 比如在叶子节点上调用addComponent()
适用场景
- 需要表示对象的部分-整体层次结构时
- 希望客户端忽略组合对象与单个对象的差异时
- 处理树形结构数据时
最佳实践
1. 合理设计组件接口
java
public interface Component {
// 共同操作
void operation();
// 容器操作(提供默认实现)
default void add(Component c) {
throw new UnsupportedOperationException();
}
}
2. 考虑透明性和安全性
java
// 透明性:在接口中声明所有方法
// 安全性:叶子节点不实现容器相关方法
3. 实现迭代器支持
java
// 可以为组合结构实现迭代器
// 方便遍历整个树形结构
组合模式 vs 其他模式
| 模式 | 目的 | 特点 |
|---|---|---|
| 组合模式 | 处理树形结构 | 统一处理单个对象和对象组合 |
| 装饰器模式 | 动态添加功能 | 包装对象,增强功能 |
| 迭代器模式 | 遍历集合 | 提供统一的遍历接口 |
总结
组合模式就像是"俄罗斯套娃",可以无限嵌套,但对外表现一致。
核心价值:
- 统一处理单个对象和对象组合
- 简化复杂树形结构的操作
- 提高代码的灵活性和可扩展性
使用场景:
- 需要处理树形结构数据时
- 希望统一处理部分和整体时
- 构建层次化系统时
简单记忆:
部分整体难区分?组合模式来解决!
树形结构轻松管,统一处理真方便。
掌握组合模式,能够让你优雅地处理复杂的层次结构,让代码更加清晰和灵活!