C++设计模式(3):迭代器模式

一、背景

有多种方法可以将对象聚合起来成为一个集合,可以将它们放进数组堆栈或者散列表等集合中,集合对象内部结构常常变化各异。拥有这些集合的客户想要遍历这些对象,最初都需要获取其内部结构,然后按照结构去遍历,这样就要暴露集合对象内部存储对象的结构,破坏了封装性。

那么如何能让客户遍历你的对象而无又无法窥视的存储对象的方式呢?最常用的方式就是将对这些集合遍历的方式封装起来,为其提供一种通用的遍历算法,这样就可以让同一种遍历算法在多种集合对象上进行操作提供可能,迭代器模式就可以很轻松做到。迭代器模式主要解决的问题是:不同的方式(算法)来遍历整个聚合对象(聚合对象中元素一致,但是集合内部结构不一样)。

二、迭代器模式

迭代器模式是一种对象的行为型模式,它提供一种方法让我们顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。

迭代器模式让我们能游走于聚合内的每一个元素,而又不暴露其内部的表示,把游走的任务放在迭代器上,而不是聚合对象上,这样简化了聚合的接口和实现,也让责任隔得其所。

三、模式角色和模式类图

1. 迭代器模式主要的角色

抽象迭代器(Iterator)
负责定义访问和遍历元素的接口,主要有hasNext()和next()抽象方法(Java是这种迭代器,C++迭代器实现有差别),待子类根据实际情况去实现这两个方法。

具体迭代器(ConcreteIterator)
实现抽象迭代器接口hasNext()和next(),并记录遍历中的当前位置;每一个聚合对象都应该对应一个具体的迭代器,集合内部结构不同,迭代器也不同;集合内部结构相同,迭代器很有可能可以重用。

抽象容器(Container)
存储和管理元素对象,声明了createIterator()用于创建一个迭代器对象,充当抽象迭代器工厂角色

具体容器(ConcreteContainer)
实现了在抽象聚合类中声明的createIterator(),该方法返回一个与该具体聚合类对应的具体迭代器ConcreteIterator实例,且该容器内部实现对具体容器对应的迭代器是透明的。

2. 迭代器模式UML图

在这里插入图片描述
例子:使用迭代器模式打印计算机专业课程表,假设课程表内部实现是string数组,如果不用迭代器模式,要想遍历课程表,我们需要客户端知道内部实现是string数组,而且需要将数组传递给客户进行操作,现在我们以迭代器模式来实现这个功能。(简单起见,规定课表内部实现为数组,且析构暂且不考虑)

#include<iostream>
#include<string>
using namespace std;
/* 抽象迭代器 */
class Iterator {
public:
	virtual bool hasNext() = 0; /* 判断是否还有下一个对象 */
	virtual string next() = 0;  /* 取出下一个对象*/
};
/* 具体迭代器 */
class CoursesIterator:public Iterator {
private:
	string* courses;
	int size = 0; /* 课程表集合最大容量 */
	int current_position = 0; /* 当前位置 */
public:
	CoursesIterator(string* cours, int size) { /* 课程表的内部结构对课程表迭代器透明 */
		this->courses = cours;
		this->size = size;
	}
	bool hasNext() { 
		return current_position != size;
	}
	string next() {
		string cour = courses[current_position];
		current_position++;
		return cour;
	}
};
/* 抽象容器 */
class Container {
public:
	virtual void add(string cour) = 0;
	virtual Iterator* createIterator() = 0;
};
/* 具体课程容器 */
class CoursesContainer:public Container {
private:
	const static int maxSize = 100;
	int courses_num = 0;
	string courses[maxSize];
public:
	CoursesContainer(){}
	void add(string t){
		if (courses_num >= maxSize) {
			cout << "CoursesContainer if full, add failed!" << endl;
		}
		else {
			courses[courses_num++] = t;
		}
	}
	Iterator* createIterator() {
		return new CoursesIterator(&courses[0], courses_num);
	}
};

int main() {
	Container *courses = new CoursesContainer();
	courses->add("C++");
	courses->add("Java");
	courses->add("Python");
	courses->add("Scala");
	Iterator* it = courses->createIterator();
	while (it->hasNext()) {
		cout << it->next() << endl;
	}
	return 0;
}

四、迭代器模式分析

优点
1.迭代器简化了聚合类。由于引入了迭代器,分离了聚合对象的遍历行为,抽象出一个迭代器类来负责,在原有的聚合对象中不需要再自行提供数据遍历等方法,这样可以简化聚合类的设计。
2.在迭代器模式中,由于引入了抽象层,增加新的聚合类和迭代器类都很方便,无须修改原有代码,满足“开闭原则”的要求。

缺点
1.由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。

五、使用场景

1.遍历聚合对象时不想暴露一个其内部的表示
2. 为遍历不同的聚合结构提供一个统一的接口(即, 支持多态迭代)----”例如A和B两个聚合对象,都是用来装对象C的,只是A和B的内部实现不一样,如果客户需遍历A和B来获得相关业务情况,那么就要先分别获得其内部结构,然后写不同的遍历算法代码;如果使用迭代器模式,可以使用面向接口进行编程,只需要一套遍历代码。

迭代器模式是与集合是必不可分的,只要实现一个集合,就需要同时提供这个集合的迭代器,就像Java中的List、Set、Map等等,这些集合都有自己的迭代器;在C++中,集合也都有自己类型的迭代器,与Java有一定差别。假如我们要实现一个这样的新的容器,当然也需要引入迭代器模式,给我们的容器实现一个迭代器。但是,由于容器与迭代器的关系太密切了,所以大多数语言在实现容器的时候都给提供了迭代器,并且这些语言提供的容器和迭代器在绝大多数情况下就可以满足我们的需要,所以现在需要我们自己去实践迭代器模式的场景还是比较少见的,我们只需要使用语言中已有的容器和迭代器就可以了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值