设计模式之组合 (Composite) 模式

143 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第24天,点击查看活动详情

简介

(GoF《设计模式》):将对象组合成树形结构以表示“部分整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。

有时候又叫做部分-整体模式,它使我们树型结构的问题中,模糊了简单元素和复杂元素的概念,客户程序可以像处理简单元素一样来处理复杂元素,从而使得客户程序与复杂元素的内部结构解耦

正章

思考
我们不仅仅要支持多个菜单,升值还要支持菜单中的菜单。你如何处理这个新的设计需求? P355

  • 【提示】在我们的新设计中,真正需要以下三点: P354
  1. 我们需要某种属性结构,可以容纳菜单、子菜单和菜单项
  2. 我们需要确定能够在每个菜单的各个项之间游走,而且至少要像现在用迭代器一样方便
  3. 我们也需要能够更有弹性地在菜单项之间游走。比方说,可能只需要遍历甜点菜单,或者可以遍历餐厅的整个菜单(包括甜点菜单在内)
  • 提供一个接口,统一菜单和菜单项的超类
  1. 接口包含菜单和菜单项的共同方法,菜单中执行方法是依次执行每个子项中的相同方法
  2. 接口包含菜单特有的增删改查子项的方法,菜单项中的增删改查子项的方法实现为直接抛出 UnsupportedOperationException
  3. 可以通过 instanceof 判断当前项是菜单还是菜单项 组合模式

image.png

特点

  • 把相同的操作应用在组合和个别对象上,即可以忽略对象组合和个别对象之间的差别 P357
  • 以违反单一职责设计原则换取透明性,即不但要管理层次结构,还要同时包含管理组合和叶节点的操作,以将组合和叶节点一视同仁 P367 空迭代器:空对象(命令模式中提到过)的一个例子。空迭代器, hasNext() 永远返回 false , next() 永远返回 null (个人觉得可以抛出 NoSuchElementException), remove() 永远抛出 UnsupportedOperationException 。 P372

思考

public class Waitress {
    MenuComponent allMenus;
    
    public Waitress(MenuComponent allMenus) {
        this.allMenus = allMenus;
    }
    
    public void printMenu() {
        allMenus.print();
    }
    
    public void printVegetarianMenu() {
        Iterator iterator = allMenus.createIterator();
        System.out.println("\nVEGETARIAN MENU\n----");
        while (iterator.hasNext()) {
            MenuComponent menuComponent = (MenuComponent)iterator.next();
            try {
                if (menuComponent.isVegetarian()) {
                    menuComponent.print();
                }
            } catch (UnsupportedOperationException e) {}
        }
    }
}

printVegetarianMenu() 方法中只有菜单项的 print() 方法可以被调用,绝对不能调用菜单(组合)的 print() 方法。你能说出原因吗? P373

  • 使用迭代器遍历时已经会遍历所有的节点(包括组合节点和叶节点),而组合节点会打印所有子节点信息,如果调用组合节点的 print() ,则必定会重复打印某些叶节点。 思考 配对下列模式和描述: P379
    策略模式:封装可互换的行为,并使用委托决定使用哪一个
    适配器模式:改变一个或多个类的接口
    迭代器模式:提供一个方式来遍历集合,而无须暴露集合的实现
    外观模式:简化一群类的接口
    组合模式:客户可以将对象的集合以及个别的对象一视同仁
    观察者模式:当某个状态改变时,允许一群对象能被通知到

所思所想

  • 以前在很多地方其实都用到了这种思想,二叉树、trie 树和线段树等树结构都允许将节点组成树形结构来表现”整体/部分“的层次结构,并且提供了一致的方式处理非叶节点和叶节点(非叶节点的方法内部按照特定逻辑处理和调用子节点的相同方法)