行为型模式--迭代器
1、意图
提供一种方法顺序访问一个聚合对象中的各个元素,而又不需暴露该对象的内部表示。
2、结构
3、参与者
Iterator:迭代器定义访问和遍历元素的接口;
ConcreteIterator:具体迭代器实现迭代器接口;对该聚合遍历时跟踪当前位置。
Aggregate:聚合定义创建相应迭代器对象的接口;
ConcreteAggregate:具体聚合实现创建相应迭代器的接口,该操作返回ConcreteIterator的一个适当的实例。
4、适用性
迭代器模式可用来:
访问一个聚合对象的内容而无需暴露它的内部表示;
支持对聚合对象的多种遍历;
为遍历不同的聚合结构提供一个统一地接口(即,支持多态迭代)。
5、代码示例
// 列表 template <class Item> class List { public: List (long size = DEFAULT_LIST_CAPACITY); long Count() const; Item& Get(long index)const; // ... }; // List类通过它的公共接口提供了一个合理的有效的途径以支持迭代,以实现两种遍历。 // 因此没有必再要给迭代器对底层数据结构的访问特权,也就是说,迭代器类不是List的友元。 // 为确保对不同遍历的透明使用,我们定义一个抽象的迭代器类,它定义了迭代器接口。 template <class Item> class Iterator { public: virtual void First() = 0; virtual void Next() = 0; virtual bool IsDone() const = 0; virtual Item CurrentItem() const = 0; protected: Iterator(); };
// 迭代器子类 template <class Item> class ListIterator : public Iterator<Item> { public: ListIterator(const List<Item>* aList); virtual void First(); virtual void Next(); virtual bool IsDone() const; virtual Item CurrentItem() const; private: const List<Item>* _list; long _current; }; // ListIterator的实现简单直接。它存储List和列表当前位置的索引_current template <class Item> ListIterator<Item>::ListIterator(const List<Item>* aList):_list(aList),_current(0){} // First将迭代器置于第一个元素: template <class Item> void ListIterator<Item>::First () { _current = 0; } // Next使当前元素向前推进一步: template <class Item> void ListIterator<Item>::Next () { _current++; } // IsDone检查指向当前元素的索引是否超出了列表: template <class Item> bool ListIterator<Item>::IsDone () const { return _current >= _list->Count(); } // 最后,CurrentItem返回当前索引指向的元素。若迭代已经终止,则抛出一个Iterator OutOfBounds异常: template <class Item> Item ListIterator<Item>::CurrentItem () const { if (IsDone()) { throw IteratorOutofBounds; } return _list->Get(_current); } // ReverseListIterator的实现是几乎是一样的,只不过它的First操作将_current置于列表的末尾, // 而Next操作将_currenty减一,向表头的方向前进一步。
// 假定有一个雇员(Employee)对象的List,而我们想打印出列表包含的所有雇员的信息。 void PrintEmployees (Iterator<Employee*>& i) { for (i.First (); !i.IsDone(); i.Next ()) { i.CurrentItem()->Print(); } } // 前面实现了从后向前和从前向后两种遍历的迭代器,我们可用这个操作以两种次序打印雇员信息: List<Employee*>* employees; // ... ListIterator<Employee*> forward(employees); ReverseListIterator<Employee*> backward(employees); PrintEmployees(forward); PrintEmployees(backward):
// List的SkipList子类必须提供一个实现Iterator接口的相应的迭代器SkipListIterator。 // 在内部,为了进行高效的迭代,SkipListIterator必须保持多个索引。 // 既然SkipListIterator实现了Iterator,PrintEmployee操作也可用于用SkipList存储的雇员列表。 SkipList<Employee*>* employees; // ... SkipListIterator<Employee*> iterator(employees); PrintEmployees(iterator);
// 为支持多态迭代,AbstractList定义一个Factory Method,称为CreateIterator。各个列表子类重定义这个方法以返回相应的迭代器。 template <class Item> class AbstractList { public: virtual Iterator<Item>* CreateIterator() const = 0; // ... }; // List重定义CreateIterator,返回一个ListIterator对象: template <class Item> Iterator<Item>* List<Item>::CreateIterator () const { return new ListIterator<Item>(this); } // 现在我们可以写出不依赖于具体列表表示的打印雇员信息的代码。 // we know only that we have an AbstractList AbstractList<Employee*>* employees; //... Iterator<Employee*>* iterator = employees->CreateIterator(); PrintEmployees(*iterator); delete iterator;
template <class Item> class IteratorPtr { public: Iteratorptr(Iterator<Item>* i):_i(i){} IteratorPtr(){delete _i;) Iterator<Item>* operator->(){return _i;} Iterator<Item>& operator*(){return *i;} private: // disallow copy and assignment to avoid // multiple deletions of _i: IteratorPtr(const IteratorPtr&); Iteratorptr& operator= (const IteratorPtr&): private: Iterator<Item>* _i; }; // IteratorPtr简化了打印代码: AbstractList<Employee*>* employees; //... IteratorPtr<Employee*> iterator(employees->CreateIterator()); PrintEmployees(*iterator);
// 另一个办法是定义一个一般的mixin类Traversable,它定义一个用于创建迭代器接口。聚合类通过混入(继承)Traversable来支持多态迭代。 // 下面是第二种实现办法的一个大体框架,它利用了子类生成。这里我们称内部迭代器为一个ListTraverser template <class Item> class ListTraverser { public: ListTraverser(List<Item>* aList); bool Traverse(); protected: virtual bool ProcessItem(const Item&) = 0; private: ListIterator<Item> _iterator; }; // ListTraverser以一个List实例为参数。在内部,它使用一个外部ListIterator进行遍历。 // Traverse启动遍历并对每一元素项调用ProcessItem操作。内部迭代器可在某次ProcessItem操作返回false时提前终止本次遍历。 // 而Traverse返回一个布尔值指示本次遍历是否提前终止。 template <class Item> ListTraverser<Item>::ListTraverser(List<Item>* aList):_iterator(aList){} template <class Item> bool ListTraverser<Item>::Traverse() { bool result = false; for(_iterator.First(); !_iterator.IsDone(); _iterator.Next() ) { result = ProcessItem(_iterator.CurrentItem()); if(result == false) { break; } } return result; }
// 使用一个ListTraverser来打印雇员列表中的头l0个雇员。 // 为达到这个目的,必须定义一个ListTraverser的子类并重定义其ProcessItem操作。 // 我们用一个_count实例变量中对已打印的雇员进行计数。 class PrintNEmployees : public ListTraverser<Employee*> { public: PrintNEmployees(List<Employee*>* aList, int n): ListTraverser<Employee*>(aList), _total(n), _count(0){} protected: bool ProcessItem(Employee* const&) ; private: int _total; int _count; }; bool PrintNEmployees::ProcessItem(Employee* const& e) { _count++; e->Print(); return _count < _total; } // 下面是PrintNEmployees怎样打印列表中的头l0个雇员的代码: List<Employeet>* employees; // ... PrintNEmployees pa(employees, 10); pa.Traverse();
// 内部迭代器可以封装不同类型的迭代。例如,FilteringListTraverser封装的迭代仅处理能通过测试的那些列表元素: template <class Item> class FilteringListTraverser { public: FilteringListTraverser(List<Item>* aList); bool Traverse(); protected: virtual bool ProcessItem(const Item&) = 0; virtual bool TestItem(const Item&) = 0; private: ListIterator<Item> _iterator; }; // 这个类接口除了增加了用于测试的成员函数TestItem外与ListTraverser相同,它的子类将重定义TestItem以指定所需的测试。 Traverse根据测试的结果决定是否越过当前元素继续遍历: template <class Item> void FilteringListTraverser<Item>::Traverse() { bool result = false; for(_iterator.First(); !_iterator.IsDone(); _iterator.Next() ) { if(TestItem(_iterator.CurrentItem())) { result = ProcessItem(_iterator.CurrentItem()); if (result == false) { break; } } } return result; }
// 与使用内部迭代器相比,外部迭代器使用方式如下 ListIterator<Employee*> i(employees); int count = 0; for(i.First(); !i.IsDone(); i.Next()) { count++; i.CurrentItem()->Print(); if (count > 10) { break; } }
6、总结
迭代器模式提供了一种方法可以顺序访问一个聚合对象中的各个元素,而无需暴露该对象的内部表示。
迭代器支持对聚合对象的多种遍历方式;
迭代器为聚合对象提供了遍历元素的方式,从而简化了聚合对象的接口,聚合对象本身不必再实现元素的遍历访问;
迭代器的经典用例:STL容器的迭代器
本文来自博客园,作者:流翎,转载请注明原文链接:https://www.cnblogs.com/hjx168/p/16220093.html