🔥 核心
把遍历元素的任务交给迭代器即可。
既让客户端感到方便,又能隐藏集合的底层实现。
🙁 问题场景
你计划在罗马游览数天,参观完所有的旅游景点。
但当你兴致勃勃的来到罗马,却发现自己一直在兜圈子——这是你第三次在罗马斗兽场抚摸渗透着血腥气味的墙壁,第四次在君士坦丁凯旋门前欣赏这古老帝国的残迹,第五次将手伸进真理之口祭奠与曾经深爱之人的回忆,第六次在萨图尔诺农神庙的残破基座下企图发现黄金的秘密…
你在偌大的罗马城中迷路了。
🙂 解决方案
还好,你在罗马的朋友向你提供了几条可靠的建议。
第一种选择,你可以在智能手机上购买一款罗马虚拟导游服务,它为你规划好了游览整个罗马城的最短路径;
第二种选择,你可以用部分旅行预算雇佣一位对罗马城了如指掌的本地向导,他甚至可以为你讲述每个景点的神奇故事;
第三种选择,你可以在罗马城里自由漫步,随心所欲,去感受这座古老帝国神圣的艺术,去探寻这座伟大遗迹曾经的辉煌。
所有的这些选择(智能手机、本土向导、自由漫步)都是遍历 罗马景点
这个集合的迭代器。
🌈 有趣的例子
我们常用的 微信(WeChat)
就会自带一个遍历好友列表的 迭代器(WechatIterator)
。通过这个迭代,我们就可以遍历好友列表,而不需要它内部的具体细节。这对于便利性和安全性,都是一个很好的设计。
容器(接口)
interface Container {
Iterator getIterator();
}
迭代器(接口)
interface Iterator {
boolean hasNext();
Object next();
}
微信
class WeChat implements Container {
// 好友列表
private String[] names = {"Mana", "Hana", "Alice", "Cocoa"};
@Override
public Iterator getIterator() {
return new NameIterator();
}
// 迭代器内部类
private class NameIterator implements Iterator {
private int index = 0;
@Override
public boolean hasNext() {
if (index < names.length) {
return true;
}
return false;
}
@Override
public Object next() {
if (hasNext()) {
return names[index++];
}
return null;
}
}
}
public class IteratorPatternDemo {
public static void main(String[] args) {
// 创建一个微信
WeChat weChat = new WeChat();
// 获取微信提供的迭代器
Iterator wechatIterator = weChat.getIterator();
// 使用迭代器
while (wechatIterator.hasNext()) {
System.out.println(wechatIterator.next());
}
}
}
Mana
Hana
Alice
Cocoa
☘️ 使用场景
◾️当集合背后为复杂的数据结构,且你希望对客户端隐藏其复杂性时(出于使用便利性或安全性的考虑),可以使用迭代器模式。
迭代器封装了与复杂数据结构进行交互的细节,为客户端提供多个访问集合元素的简单方法。这种方式不仅对客户端来说非常方便,而且能避免客户端在直接与集合交互时执行错误或有害的操作,从而起到保护集合的作用。
◾️使用该模式可以减少程序中重复的遍历代码。
重要迭代算法的代码往往体积非常庞大。当这些代码被放置在程序业务逻辑中时,它会让原始代码的职责模糊不清,降低其可维护性。因此,将遍历代码移到特定的迭代器中可使程序代码更加精炼和简洁。
◾️如果你希望代码能够遍历不同的甚至是无法预知的数据结构,可以使用迭代器模式。
该模式为集合和迭代器提供了一些通用接口。如果你在代码中使用了这些接口,那么将其他实现了这些接口的集合和迭代器传递给它时,它仍将可以正常运行。
🧊 实现方式
(1)声明迭代器接口。该接口必须提供至少一个方法来获取集合中的下个元素。但为了使用方便,你还可以添加一些其他方法,例如获取前一个元素、记录当前位置和判断迭代是否已结束。
(2)声明集合接口并描述一个获取迭代器的方法。其返回值必须是迭代器接口。如果你计划拥有多组不同的迭代器,则可以声明多个类似的方法。
(3)为希望使用迭代器进行遍历的集合实现具体迭代器类。迭代器对象必须与单个集合实体链接。链接关系通常通过迭代器的构造函数建立。
(4)在你的集合类中实现集合接口。其主要思想是针对特定集合为客户端代码提供创建迭代器的快捷方式。集合对象必须将自身传递给迭代器的构造函数来创建两者之间的链接。
(5)检查客户端代码,使用迭代器替代所有集合遍历代码。每当客户端需要遍历集合元素时都会获取一个新的迭代器。
🎲 优缺点
➕ 你可以并行遍历同一集合,因为每个迭代器对象都包含其自身的遍历状态。
➕ 相似的,你可以暂停遍历并在需要时继续。
➕ 单一职责原则。通过将体积庞大的遍历算法代码抽取为独立的类,你可对客户端代码和集合进行整理。
➕ 开闭原则。你可实现新型的集合和迭代器并将其传递给现有代码,无需修改现有代码。
➖ 在一定程度上增加了系统的复杂性。
➖ 对于某些特殊集合,使用迭代器可能比直接遍历的效率低。
➖ 如果你的程序只与简单的集合进行交互,应用该模式可能会矫枉过正。
🌸 补充
我们经常使用迭代器的 hasNext()
和 next()
方法,却从来没有想过为什么这样设计,不是吗?