程序设计模式(十四) C++ 迭代器(Iterator)模式
迭代器模式是提供一种方法顺序访问一个聚合对象中各个元素,而又不需要暴露该对象。
迭代器分内部迭代器和外部迭代器,内部迭代器与对象耦合紧密,不推荐使用。外部迭代器与聚合容器的内部对象松耦合,推荐使用。迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集 合内部的数据。而且,可以同时定义多个迭代器来遍历,互不冲突。对于迭代器,参考STL迭代器,只需要使用具体容器的迭代器就可以遍历该容器内的聚合对象,也可以借助迭代器实现对象的诸如添加、删除和调用。
场景:在现在的电视机中,我们使用[后一个]和[前一个]按钮可以很方便的换台,当按下[后一个]按钮时,将切换到下一个预置的频道。想象一下在陌生的城市中的旅店中看电视。当改变频道时,重要的不是几频道,而是节目内容。如果对一个频道的节目不感兴趣,那么可以换下一个频道,而不需要知道它是几频道。这个其实就是我们迭代器模式的精髓:提供一种方法顺序访问一个聚合对象中各个元素, 而又不需暴露该对象的内部表示。
类图:
迭代器模式由以下角色组成:
1.迭代器角色(Iterator):迭代器角色负责定义访问和遍历元素的接口。
2.具体迭代器角色(CAggregateAIter CAggregateBIter):具体迭代器角色要实现迭代器接口,并要记录遍历中的当前位置。
3.集合角色(Menu):集合角色负责提供创建具体迭代器角色的接口。
4.具体集合角色(CAggregateA CAggregateB):具体集合角色实现创建具体迭代器角色的接口——这个具体迭代器角色于该集合的结构相关。
实现:
1.迭代抽象:访问一个聚合对象的内容而无需暴露它的内部表示。
2.迭代多态:为遍历不同的集合结构提供一个统一的接口,从而支持同样的算法在不同的集合结构上进行操作。
3.迭代器的健壮性考虑:遍历的同时更改迭代器所在的集合结构,会导致问题。
4.当需要支持对聚合对象的多种遍历时,考虑使用迭代器模式。
实例:
我们有两个聚合A和B,有一个聚合管理的类,包含聚合A和B,而这个管理类的功能就是依次遍历它所包含的聚合中的元素并打印到控制台上。每个聚合中的元素存放的方式是不同的,有的用的是数组,有的可能是vector或者list等,因此我么为每个聚合定义了一个迭代器,管理类只需要依次获取各聚合的迭代器,使用迭代器去遍历其中的元素,而不需要去理会聚合中元素的存放方式。
c++代码如下:
#include <iostream> #include <vector> using namespace std; #define COUNT 5 class Iterator { public: virtual bool hasNext() = 0; virtual int next() = 0; }; class Menu { public: virtual Iterator* createIterator() = 0; }; class CAggregateAIter : public Iterator { public: CAggregateAIter(vector<int> vData); bool hasNext(); int next(); private: vector<int> m_vData; int m_iPosition; }; class CAggregateBIter : public Iterator { public: CAggregateBIter(int* piData); bool hasNext(); int next(); private: int m_iPosition; int m_iData[COUNT]; }; class CAggregateA : public Menu { public: CAggregateA(); Iterator* createIterator(); private: vector<int> m_vData; }; class CAggregateB : public Menu { public: CAggregateB(); Iterator* createIterator(); private: int m_iData[COUNT]; }; class AggregateMg { public: void printAggr(); void printAggr(Iterator* piter); private: CAggregateA m_aggrA; CAggregateB m_aggrB; }; CAggregateAIter::CAggregateAIter(vector<int> vData) { m_iPosition = 0; for (int i=0; i<vData.size(); ++i) { m_vData.push_back(vData.at(i)); } } bool CAggregateAIter::hasNext() { if (m_iPosition < m_vData.size()) { return true; } else { return false; } } int CAggregateAIter::next() { int iData = m_vData.at(m_iPosition); ++m_iPosition; return iData; } CAggregateBIter::CAggregateBIter(int* piData) { m_iPosition = 0; for (int i=0; i<COUNT; ++i) { m_iData[i] = piData[i]; } } bool CAggregateBIter::hasNext() { if (m_iPosition < COUNT) { return true; } else { return false; } } int CAggregateBIter::next() { int iData = m_iData[m_iPosition]; ++m_iPosition; return iData; } CAggregateA::CAggregateA() { for (int i=0; i<COUNT; ++i) { m_vData.push_back(i*(i+1)); } } Iterator* CAggregateA::createIterator() { return new CAggregateAIter(m_vData); } CAggregateB::CAggregateB() { for (int i = 0; i < COUNT; ++i) { m_iData[i] = i*(i+1); } } Iterator* CAggregateB::createIterator() { return new CAggregateBIter(m_iData); } void AggregateMg::printAggr() { Iterator* piter1 = m_aggrA.createIterator(); Iterator* piter2 = m_aggrB.createIterator(); printAggr(piter1); printAggr(piter2); delete piter1; delete piter2; } void AggregateMg::printAggr(Iterator* piter) { while(piter->hasNext()) { printf("%d\n",piter->next()); } } int main() { AggregateMg aggrMg; aggrMg.printAggr(); return 0; }
运行结果如下:
0
2
6
12
20
0
2
6
12
20
解释:
1、我们使用c++ STL的时候常用的迭代器方法是begin(),end()等。有些迭代器提供的是first(),isDone(),currentItem()等方法,其实怎么封装,最终实现的都是迭代器模式定义中的不暴露聚合内部的表示,让迭代器来实现顺序访问聚合中元素的功能,我们甚至可以为迭代器封装一些操作聚合元素的方法,例如remove()。
2、我们将聚合管理类与各个聚合类之间解耦了,如果我们为聚合管理类新加了一些其它的聚合,那么我们只需要修改很少的代码就可以完成我们的工作,而且不会影响其它的功能。
重构成本:高。