# 迭代器模式,给你更高大上的遍历体验~ ``` 写代码不少使用数组或者类似的集合对象吧?每次要遍历一遍数组怎么办?For 循环!或者while循环,一个一个访问每个位置的元素,直到数组末尾。STL里面甚至有专门的迭代器,针对具体的集合类对象,有对应使用的迭代器。STL的迭代器提供了丰富的遍历方法,如访问集合对象的首位元素、末位元素、指定位置的元素、下一个元素……怎么样,是不是感觉有了迭代器,遍历方法不再是难事了? ``` ## 1.迭代器模式概述 遍历在日常编码过程中经常使用,通常是需要对一个具有很多对象实例的集合(称为**聚合对象**)进行访问或获取。比如要取聚合对象的首位元素、判断是否在聚合对象的末尾等。针对聚合对象的遍历,迭代器模式是一种很有效的解决方案,也是一种使用频率很高的设计模式。 ``` 迭代器模式: 提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。 ``` 通过引入迭代器,可以将数据的遍历功能从聚合对象中分离出来,这样一来,聚合对象只需负责存储数据,而迭代器对象负责遍历数据,使得聚合对象的职责更加单一,符合单一职责原则。 ## 2.迭代器模式结构 迭代器模式结构中包含聚合和迭代器两个层次的结构。为方便扩展,迭代器模式常常和工厂方法模式结合。迭代器模式的UML图如下。有图可知,迭代器模式有以下几个角色: - **Iterator(抽象迭代器)**:声明了访问和遍历聚合对象元素的接口,如first()方法用于访问聚合对象中第一个元素,next()方法用于访问下一个元素,hasNext()判断是否还有下一个元素,currentItem()方法用于获取当前元素。 - **ConcreteIterator(具体迭代器)**:实现抽象迭代器声明的方法,通常具体迭代器中会专门用一个变量(称为游标)来记录迭代器在聚合对象中所处的位置。 - **Aggregate(抽象聚合类)**:用于存储和管理元素对象,声明一个创建迭代器的接口,其实是一个抽象迭代器工厂的角色。 - **ConcreteAggregate(具体聚合类)**:实现了方法createIterator(),该方法返回一个与该具体聚合类对应的具体迭代器ConcreteIterator的实例。 ![avatar](https://github.com/FengJungle/DesignPattern/blob/master/17.IteratorPattern/1.Picture/%E8%BF%AD%E4%BB%A3%E5%99%A8%E6%A8%A1%E5%BC%8FUML%E5%9B%BE.png) ## 3.迭代器模式代码实例 ``` 电视机遥控器是迭代器的一个现实应用,通过它可以实现对电视频道集合的遍历操作,电视机可以看成一个存储频道的聚合对象。本例Jungle将采用迭代器模式来模拟遥控器操作电视频道的过程。 ``` 很明显,遥控器是一个具体的迭代器,具有上一个频道previous() 、下一个频道next()、当前频道currentChannel()等功能;需要遍历的聚合对象是电视频道的集合,即电视机。本例的UML图如下: ![avatar](https://github.com/FengJungle/DesignPattern/blob/master/17.IteratorPattern/1.Picture/%E8%BF%AD%E4%BB%A3%E5%99%A8%E6%A8%A1%E5%BC%8F%E5%AE%9E%E4%BE%8BUML%E5%9B%BE.png) ### 3.1.抽象聚合类和具体聚合类 ``` #ifndef __AGGREGATE_H__ #define __AGGREGATE_H__ #include #include using namespace std; // 前向声明,因为两个类互相引用 class Iterator; class RemoteControl; // 抽象聚合类 Aggregate class Aggregate { public: Aggregate(){} virtual ~Aggregate(){} virtual Iterator* createIterator() = 0; }; // 具体聚合类 Television class Television :public Aggregate { public: Television(); Television(vector iChannelList); // 实现创建迭代器 Iterator* createIterator(); // 获取总的频道数目 int getTotalChannelNum(); void play(int i); private: vector channelList; }; #endif //__AGGREGATE_H__ ``` 实现: ``` #include "Iterator.h" Television::Television(){} Television::Television(vector iChannelList){ this->channelList = iChannelList; } Iterator* Television::createIterator(){ RemoteControl *it = new RemoteControl(); it->setTV(this); return (Iterator*)it; } int Television::getTotalChannelNum(){ return channelList.size(); } void Television::play(int i){ printf("现在播放:%s……\n", channelList[i].c_str()); } ``` ### 3.2.抽象迭代器 ``` // 抽象迭代器 class Iterator { public: Iterator(){} virtual ~Iterator(){} // 声明抽象遍历方法 virtual void first() = 0; virtual void last() = 0; virtual void next() = 0; virtual void previous() = 0; virtual bool hasNext() = 0; virtual bool hasPrevious() = 0; virtual void currentChannel() = 0; private: }; ``` ### 3.3.具体迭代器:RemoteControl ``` // 遥控器:具体迭代器 class RemoteControl :public Iterator { public: RemoteControl(){} void setTV(Television *iTv){ this->tv = iTv; cursor = -1; totalNum = tv->getTotalChannelNum(); } // 实现各个遍历方法 void first(){ cursor = 0; } void last(){ cursor = totalNum - 1; } void next(){ cursor++; } void previous(){ cursor--; } bool hasNext(){ return !(cursor == totalNum); } bool hasPrevious(){ return !(cursor == -1); } void currentChannel(){ tv->play(cursor); } private: // 游标 int cursor; // 总的频道数目 int totalNum; // 电视 Television* tv; }; ``` ### 3.4.客户端代码示例及结果 ``` #include #include "Iterator.h" int main() { vector channelList = { "新闻频道", "财经频道", "体育频道", "电影频道", "音乐频道", "农业频道", "四川卫视", "成都卫视" }; // 创建电视 Television *tv = new Television(channelList); // 创建遥控器 Iterator *remoteControl = tv->createIterator(); // 顺序遍历 printf("顺序遍历:\n"); remoteControl->first(); // 遍历电视所有频道 while (remoteControl->hasNext()){ remoteControl->currentChannel(); remoteControl->next(); } printf("\n\n"); // 逆序遍历 printf("逆序遍历:\n"); remoteControl->last(); // 遍历电视所有频道 while (remoteControl->hasPrevious()){ remoteControl->currentChannel(); remoteControl->previous(); } printf("\n\n"); system("pause"); delete tv; delete remoteControl; return 0; } ``` 结果如下图: ![avatar](https://github.com/FengJungle/DesignPattern/blob/master/17.IteratorPattern/1.Picture/%E8%BF%90%E8%A1%8C%E5%9B%BE1.png) ## 4.总结 观察上述代码可发现,迭代器类和聚合类存在相互包含相互引用的关系,因此代码里需要前向声明某个类(具体操作见上,代码资源见GitHub - FengJungle/DesignPattern: Design pattern demo code)。 - 优点: - 支持以不同的方式遍历一个聚合对象,在同一个聚合对象上可以定义多个遍历方式。 - 简化了聚合类,使得聚合类的职责更加单一; - 迭代器模式中引入抽象层,易于增加新的迭代器类,便于扩展,符合开闭原则。 - 缺点: - 将聚合类中存储对象和管理对象的职责分离,增加新的聚合类时同样需要考虑增加对应的新的迭代器类,类的个数成对增加,不利于系统管理和维护; - 设计难度较大,需要充分考虑将来系统的扩展。 - 适用环境: - 访问一个聚合对象而无需暴露它的内部结构; - 需要为一个聚合对象提供多种遍历方法。