迭代器模式,給你更高大上的遍歷體驗!

寫代碼不少使用數組或者類似的集合對象吧?每次要遍歷一遍數組怎麼辦?For 循環!或者while循環,一個一個訪問每個位置的元素,直到數組末尾。STL裏面甚至有專門的迭代器,針對具體的集合類對象,有對應使用的迭代器。STL的迭代器提供了豐富的遍歷方法,如訪問集合對象的首位元素、末位元素、指定位置的元素、下一個元素……怎麼樣,是不是感覺有了迭代器,遍歷方法不再是難事了?

1.迭代器模式概述

遍歷在日常編碼過程中經常使用,通常是需要對一個具有很多對象實例的集合(稱爲聚合對象)進行訪問或獲取。比如要取聚合對象的首位元素、判斷是否在聚合對象的末尾等。針對聚合對象的遍歷,迭代器模式是一種很有效的解決方案,也是一種使用頻率很高的設計模式。

迭代器模式:

提供一種方法順序訪問一個聚合對象中的各個元素,而又不暴露該對象的內部表示。

 通過引入迭代器,可以將數據的遍歷功能從聚合對象中分離出來,這樣一來,聚合對象只需負責存儲數據,而迭代器對象負責遍歷數據,使得聚合對象的職責更加單一,符合單一職責原則。

2.迭代器模式結構

迭代器模式結構中包含聚合和迭代器兩個層次的結構。爲方便擴展,迭代器模式常常和工廠方法模式結合。迭代器模式的UML圖如下。有圖可知,迭代器模式有以下幾個角色:

  • Iterator(抽象迭代器):聲明瞭訪問和遍歷聚合對象元素的接口,如first()方法用於訪問聚合對象中第一個元素,next()方法用於訪問下一個元素,hasNext()判斷是否還有下一個元素,currentItem()方法用於獲取當前元素。
  • ConcreteIterator(具體迭代器):實現抽象迭代器聲明的方法,通常具體迭代器中會專門用一個變量(稱爲遊標)來記錄迭代器在聚合對象中所處的位置。
  • Aggregate(抽象聚合類):用於存儲和管理元素對象,聲明一個創建迭代器的接口,其實是一個抽象迭代器工廠的角色。
  • ConcreteAggregate(具體聚合類):實現了方法createIterator(),該方法返回一個與該具體聚合類對應的具體迭代器ConcreteIterator的實例。

3.迭代器模式代碼實例

電視機遙控器是迭代器的一個現實應用,通過它可以實現對電視頻道集合的遍歷操作,電視機可以看成一個存儲頻道的聚合對象。本例Jungle將採用迭代器模式來模擬遙控器操作電視頻道的過程。

很明顯,遙控器是一個具體的迭代器,具有上一個頻道previous() 、下一個頻道next()、當前頻道currentChannel()等功能;需要遍歷的聚合對象是電視頻道的集合,即電視機。本例的UML圖如下:

3.1.抽象聚合類和具體聚合類

#ifndef __AGGREGATE_H__
#define __AGGREGATE_H__

#include <vector>
using namespace std;

// 前向聲明,因爲兩個類互相引用
class Iterator;
class RemoteControl;

// 抽象聚合類 Aggregate
class Aggregate
{
public:
	Aggregate(){}
	virtual Iterator* createIterator() = 0;
};

// 具體聚合類 Television
class Television :public Aggregate
{
public:
	Television();
	Television(vector<string> iChannelList);
	// 實現創建迭代器
	Iterator* createIterator();
	// 獲取總的頻道數目
	int getTotalChannelNum();
	void play(int i);
private:
	vector<string> channelList;
};

#endif //__AGGREGATE_H__

實現:

#include "Iterator.h"

Television::Television(){}

Television::Television(vector<string> iChannelList){
	this->channelList = iChannelList;
}

Iterator* Television::createIterator(){
	RemoteControl *it = new RemoteControl();
	it->setTV(this);
	return (Iterator*)it;
}

int Television::getTotalChannelNum(){
	return channelList.size();
}

void Television::play(int i){
	printf("現在播放:%s……\n", channelList[i].c_str());
}

 3.2.抽象迭代器

// 抽象迭代器
class Iterator
{
public:
	Iterator(){}
	// 聲明抽象遍歷方法
	virtual void first() = 0;
	virtual void last() = 0;
	virtual void next() = 0;
	virtual void previous() = 0;
	virtual bool hasNext() = 0;
	virtual bool hasPrevious() = 0;
	virtual void currentChannel() = 0;
};

3.3.具體迭代器:RemoteControl

// 遙控器:具體迭代器
class RemoteControl :public Iterator
{
public:
	RemoteControl(){}
	void setTV(Television *iTv){
		this->tv = iTv;
		cursor = -1;
		totalNum = tv->getTotalChannelNum();
	}
	// 實現各個遍歷方法
	void first(){
		cursor = 0;
	}
	void last(){
		cursor = totalNum - 1;
	}
	void next(){
		cursor++;
	}
	void previous(){
		cursor--;
	}
	bool hasNext(){
		return !(cursor == totalNum);
	}
	bool hasPrevious(){
		return !(cursor == -1);
	}
	void currentChannel(){
		tv->play(cursor);
	}
private:
	// 遊標
	int cursor;
	// 總的頻道數目
	int totalNum;
	// 電視
	Television* tv;
};

3.4.客戶端代碼示例及結果 

#include <iostream>
#include "Iterator.h"

int main()
{
	vector<string> channelList = { "新聞頻道", "財經頻道", "體育頻道", "電影頻道", "音樂頻道", "農業頻道", "四川衛視", "成都衛視" };
	// 創建電視
	Television *tv = new Television(channelList);
	// 創建遙控器
	Iterator *remoteControl = tv->createIterator();

	// 順序遍歷
	printf("順序遍歷:\n");
	remoteControl->first();
	// 遍歷電視所有頻道
	while (remoteControl->hasNext()){
		remoteControl->currentChannel();
		remoteControl->next();
	}

	printf("\n\n");

	// 逆序遍歷
	printf("逆序遍歷:\n");
	remoteControl->last();
	// 遍歷電視所有頻道
	while (remoteControl->hasPrevious()){
		remoteControl->currentChannel();
		remoteControl->previous();
	}

	printf("\n\n");
	system("pause");
	return 0;
}

結果如下圖:

 4.總結

觀察上述代碼可發現,迭代器類和聚合類存在相互包含相互引用的關係,因此代碼裏需要前向聲明某個類(具體操作見上,代碼資源見https://github.com/FengJungle/DesignPattern)。

優點:

  • 支持以不同的方式遍歷一個聚合對象,在同一個聚合對象上可以定義多個遍歷方式。
  • 簡化了聚合類,使得聚合類的職責更加單一;
  • 迭代器模式中引入抽象層,易於增加新的迭代器類,便於擴展,符合開閉原則。

缺點:

  • 將聚合類中存儲對象和管理對象的職責分離,增加新的聚合類時同樣需要考慮增加對應的新的迭代器類,類的個數成對增加,不利於系統管理和維護;
  • 設計難度較大,需要充分考慮將來系統的擴展。

適用環境:

以下場景可以考慮使用迭代器模式:

  • 訪問一個聚合對象而無需暴露它的內部結構;
  • 需要爲一個聚合對象提供多種遍歷方法。

歡迎關注知乎專欄:Jungle是一個用Qt的工業Robot

歡迎關注Jungle的微信公衆號:Jungle筆記

 

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