Iterator设计模式是GOF里面的23中设计模式之一, 其定义是”提供一种方法访问一个容器对象中各个元素,而又不暴露该对象的内部细节。”
在没有学Iterator设计模式之前,如果要遍历容器里面的各个元素,我们可能想到的就是用一个链表把这些元素连起来,然后通过->next来遍历这些元素。但是这样就暴露了容器的内部细节。另外,如果有多个client同时遍历这个容器,可能还要给这个链表加个锁什么的,这样又浪费了系统资源。这些问题的原因都是容器的内部和它的遍历紧耦合了。
Iterator设计模式则把容器的内部实现和它的遍历实现了解耦,其结构图如下:
上面的各个部分说明如下:
Iterator:定义迭代器访问和遍历元素的接口;
ConcreteIterator:实现具体的迭代器;
Aggregate:定义的容器,创建相应迭代器对象的接口;
ConcreteAggregate:具体的容器实现创建相应迭代器的接口,该操作返回ConcreteIterator的一个适当的实例。
一个Iterator设计模式的基本代码如下:
template<typename T>
class Iterator
{
public:
virtual void first() = 0;
virtual void next() = 0;
virtual bool isDone() const = 0;
virtual T& current() = 0;
};
template<typename T>
class MyCollection{
public:
Iterator<T> GetIterator(){
//...
}
};
template<typename T>
class CollectionIterator : public Iterator<T>{
MyCollection<T> mc;
public:
CollectionIterator(const MyCollection<T> & c): mc(c){ }
void first() override {
}
void next() override {
}
bool isDone() const override{
}
T& current() override{
}
};
void MyAlgorithm()
{
MyCollection<int> mc;
Iterator<int> iter= mc.GetIterator();
for (iter.first(); !iter.isDone(); iter.next()){
cout << iter.current() << endl;
}
}
在上面代码中,CollectionIterator就是结构图里面的ConcreteAggregate,里面包含了MyCollection的对象mc。在for loop中,里面的first(),isDone()和next()都是重载后的虚函数,实现运行时绑定。这样,用户并不知道MyCollection的内部细节,也可以实现遍历。
不过我想补充的是Iterator这种设计模式在JAVA里面用的很多,但是在C++里面已经过时了。为什么呢?因为Iterator设计模式是基于虚函数的,属于运行时绑定。我们知道虚函数运行时绑定就要查虚函数表,如果在一个很大的loop里面的话,这种虚函数绑定累计起来就耽误了很多时间,效率非常低。
而C++的STL是采用泛型编程,属于编译时绑定,效率非常高。那么为什么泛型编程可以在编译时绑定呢?因为它是基于Traits实现的,内部用到了类模板和偏特化。所以STL里面的Iterator并不是用的Iterator设计模式!