设计模式 18 迭代器模式 Iterator Pattern
1.定义
迭代器模式 (Iterator Pattern) 是一种行为型设计模式,它提供了一种访问集合元素的标准方法,而无需暴露集合的内部表示。
提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。
核心思想来自将遍历集合的逻辑从集合本身中分离出来,封装到一个独立的迭代器对象中。
迭代器对象负责维护遍历的当前位置,并提供访问下一个元素的方法。
想象一个书架,上面摆满了书籍。如果你想阅读这些书籍,你可以选择不同的方式:
- 直接访问: 你直接走到书架前,从最左边开始一本一本地拿书阅读。
- 使用书签: 你用书签标记当前阅读的位置,下次阅读时直接从书签处开始。
2.内涵
迭代器模式就像使用书签一样,它提供了一种间接访问集合元素的方式:
迭代器对象: 就像书签,它记录了当前访问的位置。
- next() 方法: 就像翻到下一页,它返回下一个元素。
- hasNext() 方法: 就像判断是否有下一页,它判断是否还有下一个元素。
3.案例分析
/Iterator Design Pattern/include <iostream>
include <string>
include <vector>template <typename T, typename U>
class Iterator {public:typedef typename std::vector<T>::iterator iter_type;Iterator(U p_data, bool reverse = false) : m_p_data_(p_data) {m_it_ = m_p_data_->m_data_.begin();}void First() {m_it_ = m_p_data_->m_data_.begin();}void Next() {m_it_++;}bool IsDone() {return (m_it_ == m_p_data_->m_data_.end());}iter_type Current() {return m_it_;}private:U m_p_data_;iter_type m_it_;
};template <class T>
class Container {friend class Iterator<T, Container>;public:void Add(T a) {m_data_.push_back(a);}Iterator<T, Container> CreateIterator() {return new Iterator<T, Container>(this);}private:std::vector<T> m_data_;
};class Data {public:Data(int a = 0) : m_data_(a) {}void set_data(int a) {m_data_ = a;}int data() {return m_data_;}private:int m_data_;
};void ClientCode() {std::cout << "________________Iterator with int______________________________________" << std::endl;Container<int> cont;for (int i = 0; i < 10; i++) {cont.Add(i);}Iterator<int, Container<int>> it = cont.CreateIterator();for (it->First(); !it->IsDone(); it->Next()) {std::cout << it->Current() << std::endl;}Container<Data> cont2;Data a(100), b(1000), c(10000);cont2.Add(a);cont2.Add(b);cont2.Add(c);std::cout << "________________Iterator with custom Class______________________________" << std::endl;Iterator<Data, Container<Data>> it2 = cont2.CreateIterator();for (it2->First(); !it2->IsDone(); it2->Next()) {std::cout << it2->Current()->data() << std::endl;}delete it;delete it2;
}int main() {ClientCode();return 0;
}
上述代码 uml 类图如下
4.注意事项
迭代器模式 Iterator Pattern 使用时需要注意什么?
使用迭代器模式时,需要注意以下几点:
1. 迭代器状态的维护:
迭代器对象需要维护当前遍历的位置,确保每次调用 `next()` 方法都能返回下一个元素。
迭代器对象需要处理遍历结束的情况,例如在 `hasNext()` 方法中返回 `false`,或在 `next()` 方法中抛出异常。
2. 迭代器的类型:
迭代器可以是内部迭代器,也称为迭代器方法,它将遍历逻辑封装在集合类内部,例如 Java 中的 `forEach` 方法。
迭代器可以是外部迭代器,它是一个独立的类,负责遍历集合,例如 Java 中的 `Iterator` 接口。
3. 迭代器的可复用性:
迭代器对象通常是不可复用的,因为它们维护了遍历状态。
如果需要多次遍历同一个集合,需要创建新的迭代器对象。
4. 迭代器的并发访问:
如果多个线程同时访问同一个迭代器对象,可能会导致数据不一致或异常。
在并发环境中使用迭代器时,需要进行同步处理,例如使用锁机制来保护迭代器对象。
5. 迭代器的性能:
迭代器的性能取决于集合的类型和迭代器的实现方式。
对于一些集合类型,例如链表,迭代器可能比直接访问元素效率低。
6. 迭代器的灵活性:
迭代器模式可以提供灵活的遍历方式,例如正序、逆序、随机访问等。
可以根据需要自定义迭代器,实现不同的遍历逻辑。
总结:
迭代器模式是一种强大的设计模式,它可以简化对集合的遍历操作,并提供灵活的遍历方式。在使用迭代器模式时,需要关注迭代器状态的维护、类型、可复用性、并发访问和性能等方面,以确保代码的正确性和效率。
5.最佳实践
在使用迭代器模式时,除了前面提到的需要注意的地方,还需要遵循以下经验:
1. 明确迭代器职责
迭代器应该只负责遍历集合,不应该包含其他操作,例如修改集合元素。
如果需要修改集合元素,应该使用专门的方法,而不是通过迭代器来实现。
2. 保持迭代器简单
迭代器应该尽可能简单易懂,避免过度设计。
迭代器应该只包含必要的操作,例如 hasNext()、next() 等。
3. 考虑迭代器的性能
如果需要频繁遍历集合,应该选择性能较高的迭代器实现。
对于一些特殊类型的集合,例如链表,可能需要使用自定义迭代器来提高性能。
4. 避免迭代器中的副作用
迭代器不应该修改集合的状态,例如添加或删除元素。
如果需要修改集合,应该使用专门的方法,而不是通过迭代器来实现。
5. 使用迭代器模式的最佳实践
优先使用语言提供的内置迭代器,例如 Java 中的 Iterator 接口。
如果需要自定义迭代器,应该遵循迭代器模式的规范,并提供必要的操作。
避免在迭代器中使用 break 或 continue 语句,因为它们会破坏迭代器的正常逻辑。
6. 迭代器与并发
在并发环境中使用迭代器时,需要格外小心,因为多个线程可能会同时访问同一个迭代器对象。
如果需要在并发环境中使用迭代器,应该使用线程安全的迭代器实现,例如 Java 中的 ConcurrentIterator 接口。
6.总结
迭代器模式是一种非常实用的设计模式,它可以帮助我们简化对集合的遍历操作,并提高代码的可读性和可维护性。在使用迭代器模式时,遵循以上经验可以帮助我们写出更加健壮和高效的代码。