重温设计模式--迭代器模式
文章目录
- 迭代器模式(Iterator Pattern)概述
- 迭代器模式的结构
- 迭代器模式UML图
- C++ 代码示例
- 应用场景
迭代器模式(Iterator Pattern)概述
-
定义:
迭代器模式是一种行为型设计模式,它提供了一种方法来顺序访问一个聚合对象(如数组、列表、树等各种容器类型的数据结构)中的各个元素,而又无需暴露该聚合对象的内部表示。简单来说,就是将遍历数据结构的操作和数据结构本身分离开来,让遍历的逻辑更加独立和通用。 -
作用:
- 解耦数据结构与遍历逻辑:使得数据结构(如容器类)的设计可以专注于存储和管理元素,而不用关心如何遍历这些元素。同时,遍历的逻辑可以独立于具体的数据结构进行修改和扩展,例如,对于不同类型的容器(数组、链表等),可以使用相同的迭代器接口来进行遍历,提高了代码的可维护性和复用性。
- 支持多种遍历方式:同一个聚合对象可以有多种不同的迭代器实现,以提供不同的遍历顺序或规则。比如对于一个二叉树结构,可以有中序遍历、前序遍历、后序遍历等不同的迭代器,方便根据具体需求来访问数据。
- 符合开闭原则:当需要对聚合对象添加新的遍历方式或者修改遍历逻辑时,只需要创建新的迭代器类或者修改已有的迭代器类,而不需要改动聚合对象的代码,对扩展开放,对修改关闭。
迭代器模式的结构
-
抽象聚合(Aggregate)类:
它是所有聚合对象的抽象,定义了创建迭代器对象的接口,通常会有一个类似createIterator()
这样的抽象方法,用于返回对应的迭代器对象,供客户端获取来遍历聚合对象中的元素。 -
具体聚合(Concrete Aggregate)类:
实现了抽象聚合类的接口,它内部维护了数据元素的存储结构,并且实现了创建迭代器的方法,返回的是与自身数据结构相匹配的具体迭代器对象,这样客户端就能通过这个迭代器来访问它所包含的元素。 -
抽象迭代器(Iterator)类:
定义了遍历聚合对象的接口,通常包含诸如first()
(定位到第一个元素)、next()
(移动到下一个元素)、isDone()
(判断是否遍历完所有元素)、currentItem()
(获取当前元素)等抽象方法,这些方法规范了遍历操作的基本行为,所有具体的迭代器都要实现这些方法。 -
具体迭代器(Concrete Iterator)类:
实现了抽象迭代器类定义的接口,针对具体的聚合对象的存储结构,实现了具体的遍历逻辑,通过内部指针或索引等方式来跟踪当前遍历的位置,从而正确地实现各个遍历操作方法,使得客户端可以按照期望的方式遍历聚合对象中的元素。
迭代器模式UML图
C++ 代码示例
以下以一个简单的自定义数组容器为例,来展示迭代器模式的代码实现。
#include <iostream>
// 抽象迭代器类
class Iterator
{
public:
virtual void first() = 0;
virtual void next() = 0;
virtual bool isDone() = 0;
virtual int currentItem() = 0;
virtual ~Iterator() {}
};
// 抽象聚合类
class Aggregate
{
public:
virtual Iterator* createIterator() = 0;
virtual ~Aggregate() {}
};
// 具体聚合类:自定义数组容器
class MyArray : public Aggregate
{
private:
int* elements;
int size;
public:
MyArray(int* arr, int s) : elements(arr), size(s) {}
Iterator* createIterator() override
{
return new ArrayIterator(this);
}
int getSize()
{
return size;
}
int at(int index)
{
return elements[index];
}
};
// 具体迭代器类:针对自定义数组的迭代器
class ArrayIterator : public Iterator
{
private:
MyArray* array;
int index;
public:
ArrayIterator(MyArray* arr) : array(arr), index(0) {}
void first() override
{
index = 0;
}
void next() override
{
index++;
}
bool isDone() override
{
return index >= array->getSize();
}
int currentItem() override
{
return array->at(index);
}
};
int main()
{
int arr[] = {1, 2, 3, 4, 5};
MyArray myArray(arr, 5);
Iterator* it = myArray.createIterator();
for (it->first();!it->isDone(); it->next())
{
std::cout << it->currentItem() << " ";
}
std::cout << std::endl;
delete it;
return 0;
}
在上述代码中:
Iterator
是抽象迭代器类,定义了迭代器的基本操作接口,规范了遍历的通用行为。Aggregate
是抽象聚合类,声明了创建迭代器的抽象方法,作为所有聚合对象的抽象基类。MyArray
是具体聚合类,它内部持有一个整数数组作为实际存储的数据结构,实现了createIterator
方法,返回一个针对自身结构的ArrayIterator
对象,以便外部能获取迭代器来遍历数组元素。ArrayIterator
是具体迭代器类,它通过维护一个索引变量来跟踪当前遍历的位置,实现了first
、next
、isDone
、currentItem
等方法,按照顺序正确地遍历MyArray
中的元素。在main
函数中,创建了一个MyArray
对象,获取其迭代器并通过循环利用迭代器的方法来遍历数组元素并输出,展示了迭代器模式在自定义数组容器中的简单应用,体现了数据结构与遍历逻辑分离以及通过统一接口进行遍历的特点。
应用场景
- 容器类库的实现:
像C++标准模板库(STL)中的各种容器(如vector
、list
、map
等)都广泛应用了迭代器模式。它们提供了对应的迭代器,使得用户可以方便地遍历容器中的元素,而且不同容器的迭代器遵循统一的接口规范,比如都可以使用begin()
和end()
方法获取迭代器的起始和结束位置,方便了算法函数(如std::for_each
、std::find
等)对不同容器元素的操作,提高了代码的通用性和复用性。 - 数据库查询结果集遍历:
在数据库编程中,当执行查询语句后会返回一个结果集,这个结果集可以看作是一个聚合对象。使用迭代器模式可以创建相应的迭代器来遍历结果集中的每一条记录(通常包含多个字段的数据),按照一定顺序获取记录中的数据,方便后续的数据处理和展示,而且不同数据库系统的结果集都可以通过实现类似的迭代器接口来提供统一的遍历方式,便于上层应用程序的开发。 - 树形结构数据遍历:
对于二叉树、多叉树等树形结构的数据,如文件系统目录树(文件夹可以包含子文件夹和文件,类似树的节点和子节点关系)、组织结构树(部门包含子部门和员工等),可以利用迭代器模式创建不同遍历顺序(如前序、中序、后序遍历二叉树,深度优先或广度优先遍历一般的树形结构)的迭代器,方便按照特定需求访问树中的各个节点元素,进行节点数据的获取、统计等操作。 - 游戏地图元素遍历:
在游戏开发中,游戏地图通常由多个地块(如方格、区域等)组成,这些地块可以看作是一个聚合对象。通过迭代器模式,可以创建合适的迭代器来遍历地图上的各个地块,例如查找特定类型的地块(有怪物的地块、有宝藏的地块等)、统计地块数量等,并且如果地图结构发生变化(如增加新区域、改变地块布局等),只需要调整迭代器的实现或者创建新的迭代器,而不需要大规模改动地图本身的管理代码。
由于迭代器模式应用场景太多,而且频次较大,C++ C# Python等语言已经把迭代器模式嵌入到语言中了,不需要用户进行专门的迭代器设计,比如C++ STL list
#include<iostream>
#include<list>
using namespace std;
int main()
{
list<int> m_list;
for(int i=0;i<10;++i)
{
m_list.push_back(i);
}
list<int>::iterator itr = m_list.begin();
for(;itr!=m_list.end(); ++itr)
{
cout<<*itr<<endl;
}
return 0;
}