👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD
🔥 2025本人正在沉淀中… 博客更新速度++
👍 欢迎点赞、收藏、关注,跟上我的更新节奏
🎵 当你的天空突然下了大雨,那是我在为你炸乌云
一、入门
什么是组合模式
组合模式(Composite Pattern)是一种结构型设计模式,它允许你将对象组合成树形结构来表示“部分-整体”的层次关系。组合模式使得客户端可以统一处理单个对象和组合对象,而无需关心它们的具体类型。
组合模式的核心思想是:将对象组织成树形结构,使得对单个对象和组合对象的操作具有一致性。
为什么有要组合模式?
假设我们正在开发一个电商平台,需要管理商品的分类和展示。商品分类是一个典型的树形结构,比如:
- 电子产品
- 手机
- 苹果手机
- 安卓手机
- 电脑
- 笔记本电脑
- 台式机
- 手机
- 服装
- 男装
- 女装
在这个系统中,每个分类(如“电子产品”)可以包含子分类(如“手机”),也可以包含具体的商品(如“苹果手机”)
不使用组合模式的实现:
// 分类类
public class Category {
private String name;
private List<Category> subCategories = new ArrayList<>();
private List<Product> products = new ArrayList<>();
public Category(String name) {
this.name = name;
}
public void addSubCategory(Category category) {
subCategories.add(category);
}
public void addProduct(Product product) {
products.add(product);
}
public void display() {
System.out.println("分类: " + name);
for (Category category : subCategories) {
category.display();
}
for (Product product : products) {
product.display();
}
}
}
// 商品类
public class Product {
private String name;
public Product(String name) {
this.name = name;
}
public void display() {
System.out.println("商品: " + name);
}
}
客户端代码
public class Client {
public static void main(String[] args) {
// 创建商品
Product iphone = new Product("iPhone 15");
Product macbook = new Product("MacBook Pro");
// 创建子分类
Category phones = new Category("手机");
phones.addProduct(iphone);
Category computers = new Category("电脑");
computers.addProduct(macbook);
// 创建顶级分类
Category electronics = new Category("电子产品");
electronics.addSubCategory(phones);
electronics.addSubCategory(computers);
// 展示分类结构
electronics.display();
}
}
存在问题
- 代码重复:
Category
和Product
需要分别定义display
方法。 - 类型判断:如果需要统一处理分类和商品,客户端需要频繁判断对象类型。
- 扩展性差:新增节点类型(如“品牌”)时,需要修改大量代码。
- 难以维护:嵌套层次较深时,代码会变得混乱。
怎么样实现组合模式?
组合模式的组成:
抽象组件(Component):
- 作用:定义所有节点(叶子节点和复合节点)的通用接口或抽象类。
- 职责:
- 声明所有节点共有的方法(如
display
、add
、remove
等)。 - 提供默认实现(可选)。
- 声明所有节点共有的方法(如
- 特点:
- 是叶子节点和复合节点的父类或接口。
- 客户端通过抽象组件与具体节点交互。
叶子节点(Leaf)
- 作用:表示树形结构中的叶子节点,没有子节点。
- 职责:
- 实现抽象组件定义的方法。
- 通常是树形结构中的最小单元。
- 特点:
- 不能包含其他节点。
- 是组合模式中的“部分”。
复合节点(Composite)
- 作用:表示树形结构中的复合节点,可以包含子节点(叶子节点或其他复合节点)。
- 职责:
- 实现抽象组件定义的方法。
- 管理子节点(如添加、删除、遍历子节点)。
- 特点:
- 可以包含其他节点。
- 是组合模式中的“整体”。
【案例】电商分类 - 改
抽象组件(Component):定义所有节点(分类和商品)的通用行为。
public abstract class ProductCategory {
protected String name;
public ProductCategory(String name) {
this.name = name;
}
public abstract void display();
}
叶子节点(Leaf):表示具体的商品,没有子节点。
public class Product extends ProductCategory {
public Product(String name) {
super(name);
}
@Override
public void display() {
System.out.println("商品: " + name);
}
}
复合节点(Composite):表示分类,可以包含子分类或商品。
import java.util.ArrayList;
import java.util.List;
public class Category extends ProductCategory {
private List<ProductCategory> children = new ArrayList<>();
public Category(String name) {
super(name);
}
public void add(ProductCategory category) {
children.add(category);
}
@Override
public void display() {
System.out.println("分类: " + name);
for (ProductCategory child : children) {
child.display();
}
}
}
客户端使用
public class Client {
public static void main(String[] args) {
// 创建商品
ProductCategory iphone = new Product("iPhone 15");
ProductCategory macbook = new Product("MacBook Pro");
// 创建子分类
Category phones = new Category("手机");
phones.add(iphone);
Category computers = new Category("电脑");
computers.add(macbook);
// 创建顶级分类
Category electronics = new Category("电子产品");
electronics.add(phones);
electronics.add(computers);
// 展示分类结构
electronics.display();
}
}
// 结果
// 分类: 电子产品
// 分类: 手机
// 商品: iPhone 15
// 分类: 电脑
// 商品: MacBook Pro
二、总结
组合模式的优点
- 统一处理单个对象和组合对象
- 核心优势:客户端无需区分处理的是叶子节点(单个对象)还是复合节点(组合对象)。
- 案例:在文件系统中,无论是文件(叶子节点)还是文件夹(复合节点),都可以统一调用
display()
方法展示信息。
- 简化客户端代码
- 核心优势:客户端不需要写复杂的条件判断(如if-else)来处理不同类型的节点。
- 案例:在电商平台的商品分类系统中,客户端只需调用display()方法,无论是单品还是套餐都能自动递归处理。
- 扩展性强
- 核心优势:新增节点类型时,只需继承抽象组件,符合开闭原则。
- 案例:若要在组织架构中新增“项目组”类型,只需继承
OrganizationUnit
,不影响现有代码。
- 树形结构清晰
- 核心优势:天然支持递归和嵌套,能直观表示“部分-整体”的层次关系。
- 案例:UI框架(如Java Swing)通过组合模式构建组件树,方便渲染和事件传递。
组合模式的缺点
- 设计复杂性
- 问题:需要抽象组件同时定义叶子节点和复合节点的行为,可能导致接口臃肿。
- 案例:如果叶子节点(如
File
)需要实现复合节点的add()
方法,只能抛出异常(UnsupportedOperationException
),不够优雅。
- 类型系统的限制
- 问题:客户端可能仍需通过
instanceof
检查类型,破坏透明性。 - 案例:在文件系统中,若需要对目录(复合节点)执行特殊操作(如计算总大小),客户端可能被迫检查类型。
- 问题:客户端可能仍需通过
- 过度泛化
- 问题:如果业务场景没有明确的层次结构,强行使用组合模式会导致设计过度复杂。
- 案例:简单的商品列表不需要组合模式,直接使用集合即可。
组合模式的适用场景
- 树形结构的数据
- 场景:需要表示对象的“部分-整体”层次关系。
- 典型案例:
- 文件系统(文件 + 文件夹)
- UI组件树(按钮 + 面板)
- 组织架构(员工 + 部门)
- 需要统一操作所有节点
- 场景:希望对所有节点执行相同操作(如渲染、计算总价、权限校验)。
- 典型案例:
- 电商平台计算单品和套餐的总价格。
- 游戏场景中渲染所有游戏对象(角色、道具、地图)。
- 动态组合嵌套结构
- 场景:需要动态地添加、删除或嵌套节点。
- 典型案例:
- 菜单系统(支持菜单项和子菜单)。
- 权限管理系统(角色可以包含多个权限,权限可以嵌套)。