Java组合案例

wen java案例 2

本文目录导读:

Java组合案例

  1. 目录导读
  2. 组合模式核心概念
  3. UML结构解析(透明式 vs 安全式)
  4. 需求场景模拟
  5. Java代码精讲
  6. 业务案例:电商后台的类目树
  7. 组合模式与递归遍历的陷阱
  8. 高频面试问答

目录导读

  1. 组合模式核心概念 – 什么是结构型设计模式中的“树形递归”
  2. UML结构解析 – 透明式与安全式的取舍
  3. 需求场景模拟 – 文件系统/组织架构/菜单权限的通用建模
  4. Java代码精讲 – 抽象组件、叶子节点、容器节点的落地实现
  5. 业务案例:电商后台的类目树 – 结合数据库与缓存的全链路设计
  6. 组合模式与递归遍历的陷阱 – 常见坑点与性能调优
  7. 高频面试问答 – 组合模式与装饰器/迭代器的区别

组合模式核心概念

组合模式(Composite Pattern)允许你将对象组合成树形结构以表达“部分-整体”的层次关系,它让客户端能够以统一方式处理单个对象(叶子节点)和组合对象(容器节点),这打破了“类型判断”的耦合。

核心价值:客户端代码无需关心操作的是单个文件还是整个文件夹,其调用行为完全一致。

与继承的区别

传统继承的树形结构是静态的,而组合模式是基于对象引用的动态组合,一个Category既可以包含子Category,也可以包含Product,这种关系在运行时可以自由调整。


UML结构解析(透明式 vs 安全式)

模式风格 定义位置 优点 缺点
透明式 抽象类声明所有操作(add/remove/getChild) 叶子与容器结构完全一致 叶子节点空实现易产生异常
安全式 容器节点独有add/remove方法 接口设计更严谨 客户端需用instanceof判断类型

实战推荐:大多数企业级系统(如商品分类)使用安全式,因为叶子节点(商品)的增删操作根本没意义。


需求场景模拟

  • 文件系统:文件夹中包含子文件夹和文件
  • 组织架构:领导下属中存在不同层级的员工
  • 菜单权限:一级菜单包含二级菜单及按钮权限
  • 电商类目:一级类目 → 二级类目 → 品牌 → 具体商品SPU

这些场景的共性:数据呈递归嵌套结构,且遍历方式一致。


Java代码精讲

// 抽象组件(安全式)
public abstract class Component {
    protected String name;
    public Component(String name) { this.name = name; }
    public abstract void operation(); // 统一业务方法
}
// 叶子节点(如商品)
public class Leaf extends Component {
    public Leaf(String name) { super(name); }
    @Override
    public void operation() { System.out.println("Leaf: " + name); }
}
// 容器节点(如类目)
public class Composite extends Component {
    private List<Component> children = new ArrayList<>();
    public Composite(String name) { super(name); }
    public void add(Component comp) { children.add(comp); }
    public void remove(Component comp) { children.remove(comp); }
    @Override
    public void operation() {
        System.out.println("Composite: " + name);
        children.forEach(Component::operation); // 递归调用
    }
}

关键设计要点

  • 递归遍历:容器的operation()内部必须循环调用子节点的同名方法。
  • 深拷贝保护:如果子节点需要拷贝,建议重写clone()配合深度遍历。
  • 透明访问:容器节点对外暴露getChildren()便于进行迭代。

业务案例:电商后台的类目树

需求:管理一个支持无限级子类目的树形结构,每个类目下可挂具体商品。

数据库设计

CREATE TABLE category (
  id INT PRIMARY KEY,
  parent_id INT DEFAULT 0,
  name VARCHAR(50)
);

代码整合(简化版)

// 加载数据为树结构
public Composite buildTree(List<Category> list) {
    Map<Integer, Composite> map = new HashMap<>();
    Composite root = null;
    for (Category c : list) {
        Composite comp = new Composite(c.getName());
        map.put(c.getId(), comp);
        if (c.getParentId() == 0) root = comp;
    }
    // 二次循环建立关联
    for (Category c : list) {
        if (c.getParentId() != 0) {
            map.get(c.getParentId()).add(map.get(c.getId()));
        }
    }
    return root;
}

性能优化:在百万级类目数据中,避免递归查询数据库,采用一次性查询全部再在内存中组装为树,最后存入Redis缓存。


组合模式与递归遍历的陷阱

  1. 循环引用:子节点指向父节点会导致死循环,解决方案:使用Set记录访问过的节点身份ID。
  2. 深层次递归栈溢出:若层级超过1000层,可改用非递归栈遍历(DFS),在企业应用中,建议控制最大层级不超过10。
  3. 频繁删除/移动:需保证父子引用同步更新,避免内存中残留孤儿节点。

高频面试问答

Q1:组合模式和装饰器模式的区别?

装饰器是一条链,强调动态添加职责;组合模式是一棵树,强调部分与整体的层次关系,IO流中使用装饰器包装BufferedReader,而菜单渲染使用组合模式。

Q2:什么时候必须用组合模式?

当你的业务遍历逻辑对所有节点完全一致,且节点层级动态变化(如权限树、部门树)时,组合模式可以消除大量if-else类型判断。

Q3:组合模式会造成系统过度设计吗?

如果业务中只有一层嵌套(如列表+详情),无需使用,判断标准:是否反复出现“递归遍历”且节点行为类似。


Java组合案例的精髓在于用同一套接口描述个体与群体,在Eclipse RCP、SWT界面库、以及Spring中的CompositeCacheManager都能看到它的影子,掌握其代码结构与递归陷阱,你就能在复杂业务树形建模中游刃有余。

延伸思考:如果你需要在组合模式上增加动态权限过滤,你会选择在Composite节点添加过滤逻辑,还是使用职责链模式?欢迎在评论区分你见解。

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