Java组合模式与外观模式的实现与应用

组合模式用于统一处理树形结构中的个体与容器,外观模式用于简化子系统对外接口;二者解决不同问题,混用会导致设计僵化。

组合模式和外观模式解决的是两类完全不同的问题:组合模式用于统一处理树形结构中的个体与容器,外观模式用于简化子系统对外的交互接口。混用或误判场景会导致设计僵化、职责错乱。

什么时候该用 Composite 而不是继承或集合

当你有一组对象天然存在“部分-整体”层级关系,且希望客户端代码无需区分单个对象和一组对象时,Composite 才真正必要。常见误用是把普通列表包装成 Composite,结果徒增抽象层却无实际收益。

  • 必须让叶子(Leaf)和容器(Composite)实现同一接口(如 Component),否则无法递归调用
  • add()remove()getChild() 等操作在 Leaf 中应抛出 UnsupportedOperationException,而不是静默忽略——这能尽早暴露误用
  • 避免在 Composite 中暴露内部集合(如返回 ArrayList 引用),应只提供不可变视图或封装遍历逻辑
public interface Component {
    void operation();
    void add(Component c);
    void remove(Component c);
}

public class Leaf implements Component { public void operation() { / 具体行为 / } public void add(Component c) { throw new UnsupportedOperationException(); } public void remove(Component c) { throw new UnsupportedOperationException(); } }

Facade 不是“写个工具类”的代名词

Facade 的核心价值在于解耦:它不隐藏功能,而是收敛调用路径。如果只是把几个静态方法塞进一个类,没做协调逻辑、没屏蔽子系统依赖、没统一异常处理,那只是命名空间整理,不是外观模式。

  • 一个 Facade 类通常只持有一组子系统类的引用,不继承、不实现子系统接口
  • 方法签名应面向使用场景(如 processOrder()),而非子系统能力(如 inventory.check() + payment.charge() + notify.send()
  • Facade 方法开始接受大量参数、或需要调用方预先构造子系统对象,说明边界已泄漏,应重构
public class OrderFacade {
    private final InventoryService inventory;
    private final Payme

ntService payment; private final NotificationService notification;
public OrderFacade(InventoryService inventory, PaymentService payment, NotificationService notification) {
    this.inventory = inventory;
    this.payment = payment;
    this.notification = notification;
}

public boolean processOrder(Order order) {
    if (!inventory.checkStock(order)) return false;
    if (!payment.charge(order)) return false;
    notification.sendConfirmation(order);
    return true;
}

}

组合与外观一起用的典型场景

当你要对外暴露一个“可嵌套配置的复杂组件”,又希望使用者不必了解其内部模块协作细节时,两者才自然交汇。例如 UI 框架中一个可折叠面板(CollapsiblePanel):它既是 Component(可被加到布局树中),又封装了动画、事件代理、DOM 更新等子系统。

  • CollapsiblePanel 实现 Component 接口,支持 addChild()render(),体现组合特性
  • 但它内部通过 AnimationFacadeEventFacade 协调底层库,使用者无需知道用的是 requestAnimationFrame 还是 CSS transitions
  • 关键分界:组合定义“它是什么结构”,外观定义“它怎么和别人合作”

真正难的是判断哪一层该抽象、哪一层该封装。组合模式容易过度设计成深树,外观模式容易退化为大杂烩类——它们的价值不在代码是否存在,而在是否让调用方少写一行 if (obj instanceof Composite),或少记三个子系统初始化顺序。