概述
????????與上一篇介紹的解釋器模式一樣,迭代器模式也是一種行為設計模式。它提供了一種方法來順序訪問一個聚合對象中的各個元素,而無需暴露該對象的內部表示。簡而言之,迭代器模式允許我們遍歷集合數據結構中的元素,而不必了解這些集合的底層實現細節。
????????音樂播放器是運用迭代器模式的一個典型例子:當我們使用音樂播放器聽歌時,通常會有“下一首”、“上一首”的功能來切換歌曲。這里的歌曲播放列表就相當于一個聚合對象,而用于切換歌曲的功能就是迭代器。用戶可以輕松地遍歷整個播放列表,而無需了解歌曲是如何存儲或排序的。
基本原理
????????迭代器模式的核心思想在于:提供一種相對“透明”的方法,以便順序訪問聚合對象中的每一個元素。通過這種方法,迭代器模式實現了遍歷邏輯與數據結構之間的解耦,使得不同的聚合對象可以使用相同的遍歷邏輯。另外,迭代器模式還支持多種元素遍歷的策略。
????????迭代器模式主要由以下四個核心組件構成。
????????1、聚合接口。定義創建相應迭代器對象的接口,通常包含一個CreateIterator方法,用于返回一個迭代器實例。
????????2、具體聚合。實現了聚合接口,并負責生成一個具體的迭代器對象,這個迭代器能夠遍歷與該具體聚合相關的集合。具體聚合不僅持有集合的數據,還可能包含添加、刪除元素等操作。
????????3、迭代器接口。定義了訪問和遍歷元素的接口,通常包括HasNext和Next兩個基本方法。前者用來判斷是否還有下一個元素,后者則返回當前元素,并指向下一個位置。還可能包括Remove方法,用于移除當前元素。
????????4、具體迭代器。實現了迭代器接口,并跟蹤遍歷的狀態。具體迭代器必須記住它所遍歷的聚合對象,并提供訪問該聚合對象元素的方法。比如:具體迭代器可能會保存一個索引或指針,用以追蹤當前遍歷到的位置。
????????基于上面的核心組件,迭代器模式的實現主要有以下四個步驟。
????????1、定義聚合接口。首先需要定義一個聚合接口,其中至少包含一個CreateIterator方法。這個方法的作用是:為所有具體聚合類提供一種通用的方式來創建相應的迭代器。
????????2、實現具體聚合。實現一個或多個具體聚合類,這些類實現了上述聚合接口。每個具體聚合類都應包含自己的數據集合,并且要實現CreateIterator方法。
????????3、定義迭代器接口。定義一個迭代器接口,該接口應該包括遍歷所需的基本方法,如HasNext和Next。如果有需要,還可以包括Remove方法。
????????4、實現具體迭代器。最后,我們需要為每個具體聚合實現一個具體的迭代器類。每個具體迭代器都應該實現迭代器接口,并維護遍歷過程中的狀態信息,比如:當前遍歷的位置。
實戰解析
????????在下面的實戰代碼中,我們使用迭代器模式模擬了音樂播放器的實現。
????????首先,我們定義了兩個接口:Iterator和SongCollection。前者規定了迭代器應具備的基本操作,后者則定義了創建迭代器的方法CreateIterator。
????????接著,我們定義了具體聚合類ConcreteSongCollection。它實現了SongCollection接口,并包含一個私有的成員變量m_vctSong,用于存儲歌曲列表。同時,聲明了ConcreteIterator為友元類,以便直接訪問其私有成員。
????????具體迭代器類ConcreteIterator實現了Iterator接口,維護對具體聚合對象的引用及當前迭代位置的迭代器m_itrCur。ConcreteIterator提供了具體的迭代邏輯,包括判斷是否還有下一首歌、獲取下一首歌、重置到列表開頭的功能。
????????最后,PlayMusic函數模擬了音樂播放功能,通過調用迭代器的方法遍歷并播放所有歌曲。在main函數中,實例化了具體的歌曲集合ConcreteSongCollection,添加了幾首歌曲,并創建了對應的迭代器來執行播放操作。
#include <iostream>
#include <vector>
#include <string>using namespace std;// 迭代器接口
class Iterator
{
public:virtual bool HasNext() = 0;virtual string Next() = 0;virtual void Reset() = 0;
};// 聚合接口
class SongCollection
{
public:virtual Iterator* CreateIterator() = 0;
};// 具體聚合
class ConcreteSongCollection : public SongCollection
{friend class ConcreteIterator;public:void AddSong(const string& strSong){m_vctSong.push_back(strSong);}Iterator* CreateIterator() override;private:vector<string> m_vctSong;
};// 具體迭代器
class ConcreteIterator : public Iterator
{
public:ConcreteIterator(const ConcreteSongCollection& collection): m_collection(collection), m_itrCur(m_collection.m_vctSong.begin()) {}bool HasNext() override{return m_itrCur != m_collection.m_vctSong.end();}string Next() override{if (HasNext()){return *(m_itrCur++);}return "";}void Reset() override{m_itrCur = m_collection.m_vctSong.begin();}private:ConcreteSongCollection m_collection;vector<string>::const_iterator m_itrCur;
};Iterator* ConcreteSongCollection::CreateIterator()
{return new ConcreteIterator(*this);
}// 模擬音樂播放器播放歌曲列表
void PlayMusic(SongCollection& collection, Iterator* pIterator)
{cout << "Start playing music..." <<endl;while (pIterator->HasNext()){string strSong = pIterator->Next();cout << "Now playing: " << strSong <<endl;}// 回到第一個元素pIterator->Reset();
}int main()
{ConcreteSongCollection collection;collection.AddSong("Hey Jude");collection.AddSong("Imagine");collection.AddSong("Shape of You");Iterator* pIterator = collection.CreateIterator();PlayMusic(collection, pIterator);delete pIterator;return 0;
}
總結
????????迭代器模式將遍歷集合的邏輯從聚合對象中分離出來,使得聚合對象不需要關心如何遍歷其內部的數據結構。這增強了代碼的封裝性,減少了模塊間的耦合度。由于遍歷邏輯被移到了迭代器中,聚合對象的接口變得更加簡潔明了,只需要提供創建迭代器的方法即可,而無需暴露復雜的遍歷邏輯或內部實現細節。另外,同一聚合對象可以通過不同的迭代器實現不同的遍歷方式(比如:順序遍歷、逆序遍歷等),增加了系統的靈活性和適應性。
????????但引入迭代器模式可能會導致系統復雜度上升,尤其是在處理簡單集合時,使用迭代器可能顯得過于繁瑣,增加了不必要的間接層。在某些情況下,尤其是當集合非常龐大時,創建多個迭代器實例可能會消耗較多的內存資源。特別要注意的是:如果迭代器在遍歷時,修改了集合的內容,可能會引發一致性問題。