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。

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