调侃《Head First设计模式》之迭代器和组合模式(二)

        在上一篇“调侃《Head First设计模式》之迭代器和组合模式(一)”主要讲了用类封装集合或者数组的迭代器,使得客户端可以方便遍历集合数组元素提高程序的可扩展性。这次继续按照它的故事,引入新的模式:组合模式。

      之前菜单的结构是一个菜单包含着几个菜单项,但现在业务需要,菜单中也要包含子菜单(如下图),那该如何实现方便呢?

     调侃《Head First设计模式》之迭代器和组合模式(二)_第1张图片

    看,餐厅菜单中又包含着甜点菜单。这样,之前的菜单类一定要修改了,如何修改才可以方便迭代和扩展性高呢?

    容易想到,现在整个菜单结构成为树形结构,如下图:

    调侃《Head First设计模式》之迭代器和组合模式(二)_第2张图片

   要迭代所有元素,就必须能在树形结构的所有元素间游走。

   要方便地迭代元素,最好的方式是对于菜单项和子菜单的迭代方式统一,而可以做到这点的,可以使用组合模式。这次我们先看模式的定义:

   组合模式:允许你将对象组合成树形结构类表现“整体/部分”层次结构。组合能让客户以一致的方式处理个别对象以及组合对象。

    类图如下:

     调侃《Head First设计模式》之迭代器和组合模式(二)_第3张图片

     看,菜单就像这里的Composite,它可以拥有子菜单,菜单项就是Leaf,已经没有子菜单了。它们都继承Component,并且实现对应的方法(比如菜单是没有增加删除子项功能的,所有没有实现add和remove方法)

     开始利用组合模式设计菜单(类图如下):

    调侃《Head First设计模式》之迭代器和组合模式(二)_第4张图片

      菜单项(MenuItem)和菜单(Menu)都继承于MenuComponent,它们根据自己的需要选择性实现一些方法。为什么这么设计呢?你可能已经猜到了,可以利用多态使得它们可以统一处理。

     上代码:

     菜单和菜单项统一的抽象类:

     调侃《Head First设计模式》之迭代器和组合模式(二)_第5张图片

    之所以每个方法都抛异常,是因为一旦子类没有实现的方法被调用,会抛异常作为提醒。

    菜单项:

    调侃《Head First设计模式》之迭代器和组合模式(二)_第6张图片

    菜单:

    调侃《Head First设计模式》之迭代器和组合模式(二)_第7张图片

     这样实现的print方法其实很有问题,根本不能实现打印的遍历,打印所有菜单项。利用之前的迭代器,修改如下:

    调侃《Head First设计模式》之迭代器和组合模式(二)_第8张图片

    利用迭代器遍历菜单项,如果是子菜单,则递归地调用子菜单的print,这样通过迭代,调用根菜单就可以一次将所有菜单打印出来啦!

    这样侍女的代码就变得很简单了,只需要一个菜单就可以了:

    调侃《Head First设计模式》之迭代器和组合模式(二)_第9张图片

   之前讲过单一责任原则,你是否觉得MenuComponent这个类违背了这个原则呢?是的,组合模式这样设计确实有所违背该原则,它利用单一原则换取透明性,也就是菜单项和子菜单对于客户来说是透明的,这样客户端的处理可以一视同仁。但是这样客户端有机会对一个元素做一些不恰当的操作(菜单添加到菜单项),因此会失去一些安全性。为了安全性我们可以采取一些条件判断语句,但是这样又失去了透明性。

    

     这样实现看起来好像没有问题了,但是由于迭代是在print内部的,如果侍女要获得一个具体的菜单或者菜单项,还需要修改。

    根据上一篇迭代器的设计,可以从基类MenuComponent入手,增加createIterator方法:

    调侃《Head First设计模式》之迭代器和组合模式(二)_第10张图片

    菜单就返回一个CompositeIterator对象(具体下面那上说),菜单项则返回空迭代器。

    CompositeIterator类利用递归实现树形结构的迭代,这很像数据结构中树的遍历,它利用堆栈类保存当前迭代器,当前迭代器遍历结束后弹出,又使用堆栈顶部的,即上一层菜单的迭代器。该类代码不多,但是要读懂需要好好琢磨。

   调侃《Head First设计模式》之迭代器和组合模式(二)_第11张图片

     通过堆栈成功维护了它在遍历中的位置,以便客户可以通过调用hasNext和next来实现遍历。

     空迭代器:

     调侃《Head First设计模式》之迭代器和组合模式(二)_第12张图片

     就是什么事都不做。


    这样一来,得到一个外部迭代器,侍女可以亲自遍历菜单了!

  

     

你可能感兴趣的:(设计模式,面向对象,组合模式)