《设计模式》— 行为型模式 — 迭代器模式

一、动机

一个聚合对象,如列表(list),应该提供一种方法来让别人可以访问它的元素,而又不需要暴露它的内部结构。此外,针对不同的需求,可能要以不同的方式遍历这个列表。但是即使可以预见到所需的那些便利操作,你也可能不希望列表的接口中充斥着各种不同遍历的操作。有时还可能需要在同一个列表上同时进行多个遍历。

迭代器模式可以帮助我们解决所有这些问题。这一模式的关键思想史将对列表的访问和遍历从列表对象中分离出来并放入一个迭代器对象中。迭代器定义了一个访问该列表元素的接口。迭代器对象负责跟踪当前的元素,即它知道哪些元素已经遍历过了。

例如,一个列表类可能需要列表迭代器,它们之间的关系如下:
在这里插入图片描述
将遍历对象与列表对象分离使我们可以定义不同的迭代器来实现不同的遍历策略,而无需在列表接口中列举它们。如果我们想实现过滤列表迭代器(FilteringListIterator)可能只访问那些满足特定过滤约束条件的元素。

注意迭代器和列表是耦合在一起的,而且客户对象必须知道遍历的是一个列表而不是其他聚合结构。最好能有一种办法使得不需要改变客户代码即可改变该聚合类。可以通过将迭代器的概念推广到多态迭代来到达这个目标。

首先,我们定义一个抽象列表类 AbstractList, 它提供操作列表的公共接口。类似地,我们需要定义一个抽象的迭代器类 Iterator用于定义公共的迭代接口。然后我们可以用不同的具体容器类和迭代器类分别实现它们。
在这里插入图片描述
在创建迭代器时,我们可以借助工厂方法模式。

二、适用性

  • 访问一个聚合对象的内容而无需暴露它的内部表示
  • 支持对聚合对象的多种遍历。
  • 为遍历不同的聚合结构提供一个统一的接口。

三、结构

在这里插入图片描述

四、效果

1、支持以不同的方式遍历一个聚合

复杂的聚合可以用多种方式进行遍历。例如,代码生成和语义检查要遍历语法分析树。代码生成可以按中序或者前序来遍历语法分析树。迭代器模式使得改变遍历算法变得很容易:仅需用一个不同的迭代器的实例代替原先的实例即可。

2、简化了聚合的接口

聚合的接口被拆分成业务(自身)和访问(迭代器)两部分。

3、在同一个聚合上可以有多个遍历

每个迭代器保持自己的遍历状态,因此你可以同时进行多个遍历。但是由于它们实际上持有的是一份引用。因此,如果在多线程环境下使用迭代器模式,可能会导致数据竞争。

五、实现

1、谁该控制迭代

一个基本的问题是决定由哪一方控制迭代,是迭代器还是使用该迭代器的客户。当由客户来控制迭代时,该迭代器称为一个外部迭代器;而由迭代器控制迭代时,该迭代器成为一个内部迭代器。使用外部迭代器的客户必须主动推进遍历的步伐,显式地向迭代器请求下一个元素。相反,若使用内部迭代器,客户只需向其提交一个待执行的操作,而迭代器将对聚合中的每一个元素实施该操作。

但是,由于定义迭代器的主体必须清楚地知道聚合的实现方式,因此由客户来实现可能会造成对封装性的破坏。

2、谁定义遍历算法

迭代器并不是唯一可定义遍历算法的地方。聚合本身也可以定义遍历算法,兵在遍历过程中用迭代器来存储当前迭代的状态。我们称这种迭代器为游标,因为它仅用来指示当前位置。

如果迭代器负责遍历算法,那么将易于在相同的聚合上使用不同的迭代算法,同时也易于在不同的聚合上复用相同的算法。

3、迭代器健壮性如何

正如前面所说,在多线程中使用同一个聚合的不同迭代器是很危险的。最简单的解决方案是进行深拷贝。不过现在C++标准中支持在多线程环境中,对容器中不同元素的操作不会相互影响。

4、用于组合对象的迭代器

在组合模式中的递归聚合结构上,外部迭代器可能难以实现,因为在该结构中不同对象处于嵌套聚合的多个不同层次,因此一个外部迭代器为跟踪当前的对象必须存储一条纵贯该 Composite 的路径。有时使用一个内部迭代器会更容易写。它仅需要递归地调用自己即可,这样就隐式地将路径存储在调用栈中,而无需显式地维护当前对象位置。

六、应用

现在很多语言都支持迭代器。以C++为例,STL中所有的容器都有其对应的迭代器。同时,所有的泛型算法都是针对不同类型的迭代器实现,而非针对容器类实现。这大大减少了算法函数的数量。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值