调侃《Head First设计模式》之迭代器和组合模式(一)
发布日期:2021-05-07 03:18:36 浏览次数:24 分类:原创文章

本文共 1300 字,大约阅读时间需要 4 分钟。

      现在餐厅来了两个新的厨师A和B,他们各自有各自实现菜单的方式。A的方式是将菜单项放在一个ArrayList当中的菜单,而B则是将菜单项放在数组当中的菜单。一个菜单项的类如下:

     

       餐厅的侍女的任务是对顾客需求打印定制的菜单,甚至告诉人们哪一个菜单项是素食。招代规格如下图:

      

       两个厨师接收不同的菜单会导致什么问题呢?如果侍女要打印出所有菜单项,对于A厨师来说需要循环遍历集合中的元素,对于B来说,则需要循环遍历数组中的元素,如下图:

       

      如果增加新的菜单实现,则又要加上一个循环遍历,这样侍女的代码将难以维护和扩展。

    看过之前的文章,很容易想到要让侍女代码容易维护扩展,就要让代码摆脱遍历的具体实现。我们发现其实两个循环遍历有很多相似的地方,那何不将遍历封装起来,这样让侍女去调用封装起来的遍历不就可以了?

   

    现在我们创建一个叫做迭代器的东西,用它封装遍历实现,一个菜单只要获得它的迭代器就可以遍历菜单的所有菜单项。类似如下代码:

    

     使用上面的代码,不管具体的菜单实现是什么,都可遍历所有元菜单项。

      从代码入手,根据以前的经验,所有迭代器有一个统一的接口:

   

      创建一个具体的迭代器实现该接口,比如数组方式实现的菜单:

      

      这样我们的菜单实现可以如下所示:

     

    具体的菜单返回具体的迭代器,客户只需要一个迭代器就可以遍历所有菜单。ArrayList实现的PancakeHouseMenu代码也是极为相似,这里留给读者思考。

    再看一次新的侍女代码:

    

     看,不同的菜单在侍女这里都可以用统一的方法实现遍历啦!实现了侍女和具体的菜单实现的解耦,侍女不需要知道菜单如何实现(数组或者集合等),她要的只是一个迭代器,就可以遍历所有菜单项。整个设计的类图:

     

     具体的菜单创建具体的迭代器,侍女要的只是一个实现Iterator接口的类去遍历相应的菜单。

    以上是告诉你们迭代器的实现原理,其实java已经为我们提供了Iterator接口:

    

    而且ArrayList本身已经设计好具体的迭代器了,只需要调用一个ArrayList集合的iterator方法就行。所以用集合实现的菜单的创建迭代器方法如下图:

   

    但是数组既不没有自己的迭代器了,所以数组还需要你去实现具体的迭代器:

    

     

     这时候我们的侍女还是和具体的迭代器捆绑的,所以要使用共同的Menu接口来解耦。

    

    所以侍女的代码可以修改为:

    

      修改后的类图:

      

      这样侍女关心的只是两个接口,减少了对类的依赖,提高了可扩展性。

      我们看下官方对迭代器模式的定义:

     迭代器模式提供了一个方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的实现。把遍历元素的任务放在了迭代器而不是聚合对象,这样简化了聚合对象的接口和实现,也责任各得其所。

      典型的类图如下

      

      说到责任各得其所,就不得不提一个设计原则:

      单一责任设计原则:一个类应该只有一个引起变化的原因。

      它告诉我们一个责任只能指派给一个类。因为与一个责任意味着一个潜在的改变区域,如果一个类的责任越多,则维护起来难度必定越大。当一个类被设计成只支持一组相关的功能时,我们说它是高内聚的。相对来说更易于维护。

       这篇文章先谈迭代器模式,下篇再继续以本故事来谈组合模式。

     

上一篇:调侃《Head First设计模式》之迭代器和组合模式(二)
下一篇:调侃《Head First设计模式》之模板方法模式

发表评论

最新留言

路过按个爪印,很不错,赞一个!
[***.219.124.196]2025年04月10日 05时26分40秒