Head First 设计模式之第九章——迭代器模式与组合模式

迭代器模式

概念

对于一些容器类型,如C++中的list、vector等等,很多时候需要对这些容器中的元素进行历遍操作。此时使用迭代器模式就非常方便了。迭代器模式主要是用于将历遍过程封装起来,并提供一个通用的接口,不管是何种类型的容器,都可以用相同的接口来进行历遍操作,从而将用户与具体的容器类型解耦出来。其类图如下所示:
Head First 设计模式之第九章——迭代器模式与组合模式

迭代器模式的定义为:迭代器提供了一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节。

其中,Iterator为迭代器接口,其定义了一个迭代器对外能提供的接口,针对具体的容器类(如vector或list等)而实现具体迭代器类ConcreteIterator,从而实现对该容器类的历遍行为。此处的迭代器提供三个接口:

  1. hasNext():是否还有下一个元素;
  2. next():获取下一个元素;
  3. remove():删除当前的元素。

而Aggregate是一个关于容器类型的接口,其提供了createIterator()接口,用于将具体容器所对应的具体迭代器返回给调用者,以供调用者进行历遍。Client为最终的用户,它需要对容器进行历遍操作,但它仅仅是依赖容器接口Aggregate与迭代器接口Iterator。将具体的容器对象以Aggregate接口的类型传入到Client中,然后使用createIterator获取具体容器的迭代器对象,然后使用迭代器的接口进行历遍即可。

应用

在Head First一书中,它使用了一个女服务员(Waitress)打印菜单的例子来讲解迭代器模式。具体的场景如下:煎饼店与一个餐厅进行合并,但两家店的菜单的实现是不相同的,所以女服务员在打印菜单时,都需要为每种菜单重复实现一次历遍行为,十分的不优雅,这也不符合“只依赖抽象而不依赖具体”的面向对象设计原则,所以使用迭代器模式对其进行改进。书中是用Java来实现的,此处就我用C++来实现了。类图如下:
Head First 设计模式之第九章——迭代器模式与组合模式
合并后的餐厅中两种菜单,分别为PancakeHouseMenu与DinerMenu,他们都实现了菜单接口IMenu,但他们的菜单项不是用相同的容器来存放的,一个使用vector,而另一个则使用array,所以Waitress每次打印菜单时都要对这个容器进行分别历遍。为了方便Waitress打印菜单,此处使用了迭代器模式,创建迭代器接口IMenuIterator,其定义了如下接口:

  1. first():将当前元素的索引设置为0,即将迭代器所指的元素指向容器中的第一个元素,这是用于在历遍之前,设置了迭代器的索引的位置,方便从头到尾历遍容器中的元素;
  2. hasNext():是否有下一个元素,返回true则表示有,如果为false则表示无;
  3. next():获取下一个元素。

分别为每个具体的菜单类创建对应的具体迭代器类,当调用菜单中的createIterator()方法时,就会返回迭代器接口的智能指针,使用这个指针即可以非常方便地历遍容器中的元素。

具体的代码实现见链接:https://gitee.com/sujiewen/design-pattern-learning/tree/master/ch9-1

最后

现在很多容器类型都会自带迭代器,如C++ STL中的vector、map等等的容器,他们全部都都带有迭代器,直接使用就可以了,所以在实际应用中,需要自己手动写迭代器的情况是非常少的,从而导致在迭代器在现实中使用不多。

组合模式

概念

组合模式也叫合成模式,有时又叫整体-部分模式,主要是用来描述整体与部分的关系,特别是用树形结构来组织各个元素的关系时,那组合模式就是实现这目的的不二之选。组合模式的定义为:“将对象组合成树状结构以表示‘部分-整体’的层次结构,使得用户对单个对象与组合对象的使用具有一致性”。
Head First 设计模式之第九章——迭代器模式与组合模式

组合模式的通用类图如上图所示,Component是一个基类,其定义了叶子类leaf与组件类Composite的共同接口,而Leaf类为树状层级结构中的最小元素,其下没有子分支,Composite类是组件类,其下有子分支或者是Leaf对象。由Leaf类与Composite类对象可以组成树状层级结构,但树中每个节点的类型都是他们的基类Component,每个节点都存放了Leaf或Composite类对象(多态)。如下图所示,用户类Client访问这个叶子或树状结构或子树结构时,是通过Compoent这个基类来统一访问的,这样可以让Client访问Leaf对象或Composite对象时,可以保持访问方式的一致性。这样做也符合面向设计中的“依赖抽象而不是依赖具体实现”的设计原则。
Head First 设计模式之第九章——迭代器模式与组合模式

应用

在head first一书中,例举了“餐厅菜单“这个例子来说明组合模式的应用。餐厅菜单中,总菜单下有若干个子菜单,子菜单下又有子菜单与菜名,而服务员需要使用这个菜单,其中服务员要实现的功能是打印整个菜单(相当于历遍整个菜单)。

场景中的菜单是典型的树状层次结构,组合模式是解决这个问题的最佳选择。实现的类图如下图所示。Waitress是服务员类,它可以实现打印整个菜单的功能,Waitress依赖一个菜单部件类(MenuComponent),这个类中定义了菜单类Menu与菜单项类MenuItem(即菜名)的公共接口,Waitress通过这些接口,即可访问MenuItem与Menu对象的具体内容。Menu可以包含若干个MenuItem类对象,而MenuItem是组合模式中的Leaf。是整个树状结构的最小元素,它记录了每个菜品的具体信息。用MenuComponent类构建整个菜单树,树中每个节点具体指向了一个Leaf类或Composite类对象。通过MenuComponent这个基类,Client可以访问菜单树中所所有节点或子树。

这个例子的C++实现代码链接为:https://gitee.com/sujiewen/design-pattern-learning/tree/master/ch9-2
Head First 设计模式之第九章——迭代器模式与组合模式

最后

如果对象是用树状层次结构来组合的话,那组合模式就是一个非常适合的模式了。