C++设计模式之迭代器模式(Iterator)(行为型)

一 定义

有许多中方法可以把对象堆起来成为一个集合(Collection)。你可以把他们放进数组,堆栈,列表或散列表(Hashtable)中,这是你的自由。每一种都有他自己的优点和合适的使用时机,但总有一个时候,你的客户想要遍历这些对象,而当这么做时,你打算让客户看到你的实现吗?我们当然希望最好不要。如何能让客户遍历你的对象而又无法窥视你存储对象的方式,也将学习如何创建一些对象超集合。

迭代器模式(Iterator):提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。

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

二 ULM

角色

Iterator:迭代器定义访问和遍历元素的接口

ConcreteIterator:具体迭代器实现迭代器接口;对该聚合遍历时跟踪当前位置

Aggregate:聚合定义创建相应的迭代器对象接口

ConcreteAggregate:具体聚合实现创建相应迭代器的接口,该操作返回ConcreteIterator的一个适当的实例。
优点

  1. 支持以不同的方式遍历一个聚合对象 : 复杂的聚合可用多种方式进行遍历。迭代器模式使得改变遍历算法变得很容易 : 仅需用一个不同的迭代器的实例代替原先的实例即可。你也可以自己定义迭代器的子类以支持新的遍历。
  2.  迭代器简化了聚合的接口 有了迭代器的遍历接口,聚合本身就不再需要类似的遍历接口了。这样就简化了聚合的接口。
  3. 在同一个聚合上可以有多个遍历 每个迭代器保持它自己的遍历状态。因此你可以同时进行多个遍历。
  4. 在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码,满足“开闭原则”的要求。
  5. 分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以不暴露集合的内部结构,又可让外部代码透明地访问集 合内部的数据。

缺点

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

适用场景

  • 1.访问一个聚合对象的内容而无须暴露它的内部表示。
  • 2.需要为聚合对象提供多种遍历方式。
  • 3.为遍历不同的聚合结构提供一个统一的接口。

三 实例

煎饼屋和午餐店合并后,我们可以在同一个地方享用煎饼屋美味的煎饼早餐和好吃的餐厅午餐,合并后的新店需要定制一份新的餐单,由于煎饼屋的原菜单是用链表实现,而午餐点原菜单是用数组实现。我们需要提供统一的访问接口。

ULM图

实现代码

#include <iostream>
#include <vector>
#include <list>
#include <string>

//菜单项类
class MenuItem
{
public:
	MenuItem() 
	{}

	MenuItem(std::string name, std::string description, double price)
	: name{ name }
	, description{ description }
	, price{ price }
	{
	}

	std::string getName() const
	{
		return name;
	}

	std::string getDescription() const
	{
		return description;
	}

	double getPrice() const
	{
		return price;
	}
private:
	std::string name;
	std::string description;
	double price;
};
//迭代器基类
class Iterator
{
public:
	//是否有下一个一个菜单
	virtual bool hasNext()  = 0
	{ 
		throw std::exception("ERROR"); 
	};
	//取下一个菜单
	virtual MenuItem next()  = 0
	{ 
		throw std::exception("ERROR"); 
	};
};

//煎饼屋餐单迭代器
class PancakeHouseMenuIterator : public Iterator
{
public:
	PancakeHouseMenuIterator(std::list<MenuItem> item)
	{
		items = item;
		iter = items.begin();
	}
	MenuItem next()
	{
		auto menuItem = *iter;
		++iter;
		return menuItem;
	}

	bool hasNext()
	{
		if (iter == items.end())
		{
			return false;
		}
		else
		{
			return true;
		}
	}
private:
	std::list<MenuItem> items;
	std::list<MenuItem>::const_iterator iter;
};
//午餐店餐单迭代器
class DinerMenuIterator : public Iterator
{
public:
	DinerMenuIterator(std::vector<MenuItem> item) :position(0)
	{
		items = item;
	}
	MenuItem next()
	{
		MenuItem menuItem = items[position];
		position = position + 1;
		return menuItem;
	}

	bool hasNext()
	{
		if (position >= items.size())
		{
			return false;
		}
		else
		{
			return true;
		}
	}
private:
	std::vector<MenuItem> items;
	unsigned int position;
};

//餐单基类
class Menu
{
public:
	//创建迭代器
	virtual Iterator* createIterator() { throw std::exception("ERROR"); }
};



//煎饼屋餐单类
class PancakeHouseMenu : public Menu
{
public:
	PancakeHouseMenu()
	{
		addItem("K&B'S Breakfase", "pacakes with eggs", 2.99);
		addItem("Buleberry Breakfase", "pacakes with buleberries", 3.99);
	}
	//增加菜单
	void addItem(std::string na, std::string descrip, double ric)
	{
		MenuItem menuItem(na, descrip, ric);
		menuItems.push_back(menuItem);
	}
	//创建PancakeHouseMenuIterator迭代器
	Iterator* createIterator()
	{
		return new PancakeHouseMenuIterator(menuItems);
	}
private:
	std::list<MenuItem> menuItems;
};

//午餐点餐单类
class DinerMenu : public Menu
{
public:
	DinerMenu()
	{
		addItem("Vegetarian BLT", "Bacon with lettuce", 2.99);
		addItem("BLT", "Bacon with tomato", 3.99);
	}
	void addItem(std::string na, std::string descrip, double ric)
	{
		MenuItem menuItem(na, descrip, ric);
		menuItems.push_back(menuItem);
	}
	Iterator* createIterator()
	{
		return new DinerMenuIterator(menuItems);
	}
private:
	std::vector<MenuItem> menuItems;
};

//服务生类
class Waitress
{
public:
	Waitress(Menu* p_PancakeHouseMenu, Menu* p_DinerMenu)
	{
		pPancakeHouseMenu = p_PancakeHouseMenu;
		pDinerMenu = p_DinerMenu;
	}
	//打印菜单
	void printMenu()
	{
		Iterator* pPancakeHouseIterator = pPancakeHouseMenu->createIterator();
		Iterator* pDinerIterator = pDinerMenu->createIterator();

		std::cout << "Menu" << std::endl << "----" << std::endl << "BREAKFAST" << std::endl;
		printMenu(pPancakeHouseIterator);
		std::cout << "LUNCH" << std::endl;
		printMenu(pDinerIterator);
	}
	//因为抽象出迭代器,所以可以根据迭代器打印菜单
	void printMenu(Iterator* iter)
	{
		while (iter->hasNext())
		{
			MenuItem menuItem = (MenuItem)iter->next();
			std::cout << menuItem.getName() << "	" << menuItem.getPrice() << "	"
				<< menuItem.getDescription() << std::endl;
		}
	}
private:
	Menu* pPancakeHouseMenu;
	Menu* pDinerMenu;
};
//客户代码
int main()
{
	Menu* pPancakeHouseMenu = new PancakeHouseMenu();
	Menu* pDinerMenu = new DinerMenu();

	Waitress waitress(pPancakeHouseMenu, pDinerMenu);
	waitress.printMenu();

	return 0;
}

运行结果:

总结

1)聚合是一个管理和组织数据对象的数据结构。
2)聚合对象主要拥有两个职责:一是存储内部数据;二是遍历内部数据。
3)存储数据是聚合对象最基本的职责。
4)将遍历聚合对象中数据的行为提取出来,封装到一个迭代器中,通过专门的迭代器来遍历聚合对象的内部数据,这就是迭代器模式的本质。迭代器模式是“单一职责原则”的完美体现。

与其他模式

Composite :迭代器常被应用到象复合这样的递归结构上。
Factory Method:多态迭代器靠Factory Method来例化适当的迭代器子类。
Memento:常与迭代器模式一起使用。迭代器可使用一个 Memento来捕获一个迭代的状态。迭代器在其内部存储Memento。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章