设计模式系列(十三)迭代器模式(Iterator Pattern)
迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而不暴露器内部的表示。也就是说,迭代器模式让我们能游走于聚合内的每一个元素,而又不暴露其内部的表示,把游走的任务放在迭代器上,而不是聚合上,这样就简化了聚合的接口和实现,也让责任各得其所。
学过面向对象编程语言的朋友一定对迭代器这个设计深有体会,在C++中的STL中很多地方都用到了迭代器,如果大家想看到更多的迭代器的优秀的实例可以参考STL的源码,会受益匪浅。在C++中的集合,如vector、list、queue等,都有迭代器,我们只需要获取它们的迭代器,就可以对其进行遍历以及其他操作。大家从这个角度想一想,是不是我们在使用不同的集合时,不用管太多的细节,直接获取迭代器,然后就可以按照固定或类似的形式进行遍历或者其他操作,这样就体现了设计模式的理念,封装性比较好,对于用户来说不需要知道底层的细节,只需要知道怎么使用即可,而且STL内部的细节自然就不会暴露给我们,接口统一,方便使用,是不是觉得在C++中用迭代器进行了很多操作,那你就是其中的受益者,这也是迭代器模式的体现。
迭代器模式中有四个角色:
(1)Iterator(抽象迭代器):它定义了访问和遍历元素的接口,声明了用于遍历数据元素的方法,在具体迭代器中将实现这些方法。
(2)ConcreteIterator(具体迭代器):它实现了抽象迭代器接口,完成对聚合对象的遍历,同时在具体迭代器中通过游标来记录在聚合对象中所处的当前位置,在具体实现时,游标通常是一个表示位置的非负整数。
(3)Aggregate(抽象聚合类):它用于存储和管理元素对象,声明一个createIterator()方法用于创建一个迭代器对象,充当抽象迭代器工厂角色。
(4)ConcreteAggregate(具体聚合类):它实现了在抽象聚合类中声明的createIterator()方法,该方法返回一个与该具体聚合类对应的具体迭代器ConcreteIterator实例。
从上面这几个角色可以看出,其实迭代器模式使用了类似于工厂方法模式的那种架构,在抽象类中定义了一个抽象方法,然后延迟到子类实现。这里有几个概念需要说明一下。首先,这里说的聚合类就类似于我们见得vector、list等,当然,聚合类不局限于这些我们见到的集合,它还可以是自己定义的一个聚合类,自己实现其中的操作;其次,这里说的迭代器就是针对于每种聚合类的迭代器,每种聚合类都要实现一个具体的迭代器类。
迭代器模式的优点有:
(1)它支持以不同的方式遍历一个聚合对象,在同一个聚合对象上可以定义多种遍历方式。在迭代器模式中只需要用一个不同的迭代器来替换原有迭代器即可改变遍历算法,我们也可以自己定义迭代器的子类以支持新的遍历方式。
(2)迭代器简化了聚合类。由于引入了迭代器,在原有的聚合对象中不需要再自行提供数据遍历等方法,这样可以简化聚合类的设计。
(3)在迭代器模式中,由于引入了抽象层,增加新的聚合类和迭代器类都很方便,无须修改原有代码,满足“开闭原则”的要求。
迭代器模式的缺点有:
(1)由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
(2)抽象迭代器的设计难度较大,需要充分考虑到系统将来的扩展,例如JDK内置迭代器Iterator就无法实现逆向遍历,如果需要实现逆向遍历,只能通过其子类ListIterator等来实现,而ListIterator迭代器无法用于操作Set类型的聚合对象。在自定义迭代器时,创建一个考虑全面的抽象迭代器并不是件很容易的事情。
迭代器模式的实现要点:
(1)迭代抽象:访问一个聚合对象的内容而无需暴露它的内部表示。
(2)迭代多态:为遍历不同的集合结构提供一个统一的接口,从而支持同样的算法在不同的集合结构上进行操作。
(3)迭代器的健壮性考虑:遍历的同时更改迭代器所在的集合结构,会导致问题。
迭代器模式的适用性:
(1)访问一个聚合对象的内容而无需暴露它的内部表示。
(2)支持对聚合对象的多种遍历。
(3)为遍历不同的聚合结构提供一个统一的接口(即, 支持多态迭代)。
下面我们来看一个例子,该例分别由三个文件组成,依次是:IteratorPattern.h、IteratorPattern.cpp、IteratorPatternTest.cpp。
// 迭代器模式
// IteratorPattern.h文件
#ifndef ITERATOR
#define ITERATOR
#include
#include
#include
#include
#include
using std::string;
using std::cout;
using std::cin;
using std::endl;
using std::vector;
using std::list;
// 迭代器模式中的迭代器抽象类
// 模板用来确定迭代器遍历的集合元素类型
template < class Item >
class Iterator
{
public:
Iterator(){}
virtual ~Iterator(){}
virtual void first () = 0;
virtual void next () = 0;
virtual bool isDone() = 0;
virtual Item* currentItem() = 0;
};
// 迭代器要遍历的菜单项中的条目:餐厅的一种菜
class MenuItem
{
public:
MenuItem(string name, string description, bool vegetarian, double price)
{
this->name = name;
this->description = description;
this->vegetarian = vegetarian;
this->price = price;
}
string getName();
string getDescription();
double getPrice();
bool isVegetarian();
string toString();
private:
string name;
string description;
bool vegetarian;
double price;
};
// 迭代器模式中的迭代器具体类
// 1. vector迭代器
template < class Item >
class VectorIterator : public Iterator < Item >
{
public:
VectorIterator(vector < Item > items)
{
this->items = items;
this->position = 0;
}
void first();
void next();
bool isDone();
Item* currentItem();
private:
vector < Item > items;
int position;
};
// 1. vector迭代器
template < class Item >
void VectorIterator < Item > ::first()
{
position = 0;
}
template < class Item >
void VectorIterator < Item > ::next()
{
if (position < items.size())
{
position++;
}
}
template < class Item >
bool VectorIterator < Item > ::isDone()
{
if (position >= items.size())
{
return true;
}
else
{
return false;
}
}
template < class Item >
Item* VectorIterator < Item > ::currentItem()
{
if (position < items.size())
{
return &(items[position]);
}
else
{
return NULL;
}
}
// 2. list迭代器
template < class Item >
class ListIterator : public Iterator < Item >
{
public:
ListIterator(list < Item > items)
{
this->items = items;
this->position = 0;
}
void first();
void next();
bool isDone();
Item* currentItem();
private:
list < Item > items;
int position;
};
// 2. list迭代器
template < class Item >
void ListIterator < Item > ::first()
{
position = 0;
}
template < class Item >
void ListIterator < Item > ::next()
{
if (position < items.size())
{
items.erase(items.begin());
}
}
template < class Item >
bool ListIterator < Item > ::isDone()
{
if (position >= items.size())
{
return true;
}
else
{
return false;
}
}
template < class Item >
Item* ListIterator < Item > ::currentItem()
{
if (position < items.size())
{
return &(*(items.begin()));
}
else
{
return NULL;
}
}
// 迭代器模式中的抽象集合角色(Aggregate)
template < class Item >
class Menu
{
public:
Menu(){}
virtual ~Menu(){}
// 集合角色中用来创建迭代器角色的函数,留给子类实现
virtual Iterator < Item > * createIterator() = 0;
};
// 迭代器模式中的具体集合角色(Concrete Aggregate)
template < class Item >
class DinerMenu : public Menu < Item >
{
public:
DinerMenu()
{
addItem("Vegetarian BLT",
"(Fakin') Bacon with lettuce & tomato on whole wheat", true, 2.99);
addItem("BLT",
"Bacon with lettuce & tomato on whole wheat", false, 2.99);
addItem("Soup of the day",
"Soup of the day, with a side of potato salad", false, 3.29);
addItem("Hotdog",
"A hot dog, with saurkraut, relish, onions, topped with cheese",
false, 3.05);
addItem("Steamed Veggies and Brown Rice",
"Steamed vegetables over brown rice", true, 3.99);
addItem("Pasta",
"Spaghetti with Marinara Sauce, and a slice of sourdough bread",
true, 3.89);
}
void addItem(string name, string description, bool vegetarian, double price);
vector < Item > getMenuItems();
Iterator < Item > * createIterator();
private:
vector < Item > menuItems;
};
// vector
template < class Item >
void DinerMenu < Item > ::addItem(string name, string description, bool vegetarian, double price)
{
MenuItem mu(name, description, vegetarian, price);
menuItems.push_back(mu);
}
template < class Item >
vector < Item > DinerMenu < Item > ::getMenuItems()
{
return menuItems;
}
template < class Item >
Iterator < Item > * DinerMenu < Item > ::createIterator()
{
return new VectorIterator < Item >(menuItems);
}
template < class Item >
class PancakeHouseMenu : public Menu < Item >
{
public:
PancakeHouseMenu()
{
addItem("K&B's Pancake Breakfast",
"Pancakes with scrambled eggs, and toast",
true,
2.99);
addItem("Regular Pancake Breakfast",
"Pancakes with fried eggs, sausage",
false,
2.99);
addItem("Blueberry Pancakes",
"Pancakes made with fresh blueberries",
true,
3.49);
addItem("Waffles",
"Waffles, with your choice of blueberries or strawberries",
true,
3.59);
}
void addItem(string name, string description, bool vegetarian, double price);
list < Item > getMenuItems();
Iterator < Item > * createIterator();
private:
list < Item > menuItems;
};
// list
template < class Item >
void PancakeHouseMenu < Item > ::addItem(string name, string description, bool vegetarian, double price)
{
MenuItem mu(name, description, vegetarian, price);
menuItems.push_back(mu);
}
template < class Item >
list < Item > PancakeHouseMenu < Item > ::getMenuItems()
{
return menuItems;
}
template < class Item >
Iterator < Item > * PancakeHouseMenu < Item > ::createIterator()
{
return new ListIterator < Item >(menuItems);
}
// 服务员来使用这两个菜单
template < class Item >
class Waitress
{
public:
Waitress(PancakeHouseMenu < Item > pancakeHouseMenu,
DinerMenu < Item > dinerMenu)
{
this->pancakeHouseMenu = pancakeHouseMenu;
this->dinerMenu = dinerMenu;
}
void printMenu();
private:
PancakeHouseMenu < Item > pancakeHouseMenu;
DinerMenu < Item > dinerMenu;
void printMenu(Iterator < Item > * iterator)
{
while (!iterator->isDone()) {
Item menuItem = *(iterator->currentItem());
cout << menuItem.getName() << ", " << endl;
cout << menuItem.getPrice() << " -- " << endl;
cout << menuItem.getDescription() << endl;
iterator->next();
}
}
};
// 服务员来使用这两个菜单
template < class Item >
void Waitress < Item > ::printMenu()
{
Iterator < Item > *pancakeIterator = pancakeHouseMenu.createIterator();
Iterator < Item > *dinerIterator = dinerMenu.createIterator();
cout << "MENU\n----\nBREAKFAST" << endl;
printMenu(pancakeIterator);
cout << "\nLUNCH" << endl;
printMenu(dinerIterator);
}
#endif
// IteratorPattern.cpp文件
#include "IteratorPattern.h"
// 迭代器要遍历的菜单项中的条目:餐厅的一种菜
string MenuItem::getName()
{
return name;
}
string MenuItem::getDescription()
{
return description;
}
double MenuItem::getPrice()
{
return price;
}
bool MenuItem::isVegetarian()
{
return vegetarian;
}
string MenuItem::toString()
{
char buf[8];
sprintf_s(buf, "%d", price);
return (name + ", $" + buf + "\n " + description);
}
// IteratorPatternTest.cpp文件
#include "IteratorPattern.h"
void main()
{
PancakeHouseMenu < MenuItem > pancakeHouseMenu;
DinerMenu < MenuItem > dinerMenu;
Waitress
该例的运行结果如图1所示,UML类图如图2所示。
图1 运行结果
图2 UML类图
从图2中,可以看出该例的类的分布和关系。该例主要是一个菜单遍历系统,服务员类使用两个不同结构的菜单,为了让服务员遍历起来比较方便,就对每一种菜单都进行了迭代器的设计,然后服务员不需要知道底层的实现细节,只需要用迭代器调用相应的函数进行遍历即可,比较方便。
下面来看看这些类和迭代器模式角色的对应关系:
(1)Iterator(抽象迭代器):Iterator类,其中声明了一些纯虚函数,用于遍历操作的基本控制,留给子类实现。
(2)ConcreteIterator(具体迭代器):VectorIterator类和ListIterator类,这两个类是不同的迭代器的具体类,分别对不同的聚合进行遍历。
(3)Aggregate(抽象聚合类):Menu类,其中就声明了一个纯虚函数,即createIterator()函数。
(4)ConcreteAggregate(具体聚合类):DinerMenu类和PancakeHouseMenu类,它们实现了在抽象聚合类中声明的createIterator()函数,该方法返回一个与该具体聚合类对应的具体迭代器ConcreteIterator实例。
另外,这个例子中的MenuItem类是用来存储菜单的每一项数据的,而Waitress类是用来遍历两个菜单的。
最后,需要对这个例子中的一些C++语言方面的知识强调一下,我当时在实现的时候也是走了很多弯路。
(1)由于本例使用了C++中的类模板,所以建立大家还是老老实实把类模板的声明和实现写在一个文件里吧,本例都写在.h文件中,本来最开始是分开写的,将类的成员函数的实现写在了.cpp中,但是由于C++对分离式编译的支持并不是很好,需要用到一些技巧来控制,并且可能还会增大编译量,得不偿失,所以目前那些export或者包含.cpp文件等方法并不是很好用,经过几番尝试以后决定还是写在了一个文件里。
关于C++中的类模板的编译过程可以参考以下链接:点击打开链接
(2)一定要注意类模板的很多细节问题,建立大家自己实现一下试试。
(3)该例中对于list的一个操作利用了list集合本身的特性,只是为了举例,具体的实现参见STL源码。
参考资料:
http://blog.csdn.net/lovelion/article/details/9992243
http://blog.csdn.net/lcl_data/article/details/9310313
http://blog.csdn.net/lovelion/article/details/9992931