C++設計模式——迭代器模式


前言

最近非常感傷,總是懷念大學的日子,做夢的時候也常常夢到。夢到大學在電腦前傻傻的敲着鍵盤,寫着代碼,對付着數據結構與算法的作業;建立一個鏈表,遍歷鏈表,打印鏈表。現在把那個時候聲明的鏈表的頭文件拿出來看看:

 1 typedef struct tagNode
 2 {
 3      int value;
 4      tagNode *pPre;
 5      tagNode *pNext;
 6 }Node;
 7 
 8 class CList
 9 {
10 public:
11      CList();
12      CList(size_t n);
13      ~CList();
14 
15      bool PushBack(int value);
16      bool PopBack(int &value);
17      bool Insert(int pos, int value);
18      bool Delete(int pos);
19      bool IsEmpty();
20      int GetLength();
21 
22      void Print();
23 
24      // To iterate the list
25      bool HasNext();
26      int Next();
27 
28 private:
29      int m_iLength;
30      Node *m_pCurrent;
31      Node *m_pHead;
32      Node *m_pTail;
33 };

再回頭看看,自己寫的代碼都有點不認識了。是的,那個時候,就是直接將鏈表的創建和遍歷都放在一類中,就是為了方便,直到那天看了迭代器設計模式,讓我有了一次回過頭來重新審視自己寫過的代碼,認識自己的不足的機會。

 

迭代器模式

在GOF的《設計模式:可復用面向對象軟件的基礎》一書中對迭代器模式是這樣說的:提供一種方法順序訪問一個聚合對象中各個元素,而又不需要暴露該對象的內部表示。

一個聚合對象,就是所謂的對象容器了;作為一個容器,都應該提供一種方法來讓別人可以訪問它的元素;但是,有的時候,我是不希望遍歷容器的人知道我的容器是如何實現的;那該怎么辦?就像我在大學那樣實現的鏈表,只提供了從頭到尾的遍歷,如果我需要從尾到頭的遍歷呢?是不是我又要添加對應的方法了呢!!!容器的遍歷方式千變萬化,我們不知道需求是如何的,如果需求變了,那么我們的代碼就會發生很大的改動,所以,我們需要去改變;對於上面的代碼,當我對同一個鏈表對象進行多次遍歷時,是不是就出現了m_pCurrent對象混亂的局面呢?是的,這一切的一切,都說明,我們必須去將一個容器的內部結構與它的遍歷進行解耦,要是出現上面的情況時,我們就無法面對。就好比STL中的容器,它將容器中對象的實現和遍歷很好的解耦了,所以,我們就無法知道它的內部是如何組織對象數據的,同時,我們也可以按照我們自己的想法去遍歷容器,而不會出現任何差錯。在我們的項目中使用迭代器模式就能很好的將容器對象的內部表示與對它的遍歷進行解耦。接下來,我們再來詳細的總結迭代器模式。

UML類圖

Iterator:定義迭代器訪問和遍歷元素的接口;
ConcreteIterator:實現具體的迭代器;
Aggregate:定義的容器,創建相應迭代器對象的接口;
ConcreteAggregate:具體的容器實現創建相應迭代器的接口,該操作返回ConcreteIterator的一個適當的實例。

 

使用場合

  1. 訪問一個聚合對象的內容而無需暴露它的內部表示;
  2. 支持對聚合對象的多種遍歷(從前到后,從后到前);
  3. 為遍歷不同的聚合結構提供一個統一的接口,即支持多態迭代。

 

作用

  1. 它支持以不同的方式遍歷一個聚合,甚至都可以自己定義迭代器的子類以支持新的遍歷;
  2. 迭代器簡化了聚合的接口,有了迭代器的遍歷接口,聚合本身就不再需要類似的遍歷接口了。這樣就簡化了聚合的接口;
  3. 在同一個聚合上可以有多個遍歷,每個迭代器保持它自己的遍歷狀態;因此,我們可以同時進行多個遍歷。

 

代碼實現

  1 #include <iostream>
  2 using namespace std;
  3 
  4 typedef struct tagNode
  5 {
  6      int value;
  7      tagNode *pNext;
  8 }Node;
  9 
 10 class JTList
 11 {
 12 public:
 13      JTList() : m_pHead(NULL), m_pTail(NULL){};
 14      JTList(const JTList &);
 15      ~JTList();
 16      JTList &operator=(const JTList &);
 17 
 18      long GetCount() const;
 19      Node *Get(const long index) const;
 20      Node *First() const;
 21      Node *Last() const;
 22      bool Includes(const int &) const;
 23 
 24      void Append(const int &);
 25      void Remove(Node *pNode);
 26      void RemoveAll();
 27 
 28 private:
 29      Node *m_pHead;
 30      Node *m_pTail;
 31      long m_lCount;
 32 };
 33 
 34 class Iterator
 35 {
 36 public:
 37      virtual void First() = 0;
 38      virtual void Next() = 0;
 39      virtual bool IsDone() const = 0;
 40      virtual Node *CurrentItem() const  = 0;
 41 };
 42 
 43 class JTListIterator : public Iterator
 44 {
 45 public:
 46      JTListIterator(JTList *pList) : m_pJTList(pList), m_pCurrent(NULL){}
 47 
 48      virtual void First();
 49      virtual void Next();
 50      virtual bool IsDone() const;
 51      virtual Node *CurrentItem() const;
 52 
 53 private:
 54      JTList *m_pJTList;
 55      Node *m_pCurrent;
 56 };
 57 
 58 JTList::~JTList()
 59 {
 60      Node *pCurrent = m_pHead;
 61      Node *pNextNode = NULL;
 62      while (pCurrent)
 63      {
 64           pNextNode = pCurrent->pNext;
 65           delete pCurrent;
 66           pCurrent = pNextNode;
 67      }
 68 }
 69 
 70 long JTList::GetCount()const
 71 {
 72      return m_lCount;
 73 }
 74 
 75 Node *JTList::Get(const long index) const
 76 {
 77      // The min index is 0, max index is count - 1
 78      if (index > m_lCount - 1 || index < 0)
 79      {
 80           return NULL;
 81      }
 82 
 83      int iPosTemp = 0;
 84      Node *pNodeTemp = m_pHead;
 85      while (pNodeTemp)
 86      {
 87           if (index == iPosTemp++)
 88           {
 89                return pNodeTemp;
 90           }
 91           pNodeTemp = pNodeTemp->pNext;
 92      }
 93      return NULL;
 94 }
 95 
 96 Node *JTList::First() const
 97 {
 98      return m_pHead;
 99 }
100 
101 Node *JTList::Last() const
102 {
103      return m_pTail;
104 }
105 
106 bool JTList::Includes(const int &value) const
107 {
108      Node *pNodeTemp = m_pHead;
109      while (pNodeTemp)
110      {
111           if (value == pNodeTemp->value)
112           {
113                return true;
114           }
115           pNodeTemp = pNodeTemp->pNext;
116      }
117      return false;
118 }
119 
120 void JTList::Append(const int &value)
121 {
122      // Create the new node
123      Node *pInsertNode = new Node;
124      pInsertNode->value = value;
125      pInsertNode->pNext = NULL;
126 
127      // This list is empty
128      if (m_pHead == NULL)
129      {
130           m_pHead = m_pTail = pInsertNode;
131      }
132      else
133      {
134           m_pTail->pNext = pInsertNode;
135           m_pTail = pInsertNode;
136      }
137      ++m_lCount;
138 }
139 
140 void JTList::Remove(Node *pNode)
141 {
142      if (pNode == NULL || m_pHead == NULL || m_pTail == NULL)
143      {
144           return;
145      }
146 
147      if (pNode == m_pHead) // If the deleting node is head node
148      {
149           Node *pNewHead = m_pHead->pNext;
150           m_pHead = pNewHead;
151      }
152      else
153      {
154           // To get the deleting node's previous node
155           Node *pPreviousNode = NULL;
156           Node *pCurrentNode = m_pHead;
157           while (pCurrentNode)
158           {
159                pPreviousNode = pCurrentNode;
160                pCurrentNode = pCurrentNode->pNext;
161                if (pCurrentNode == pNode)
162                {
163                     break;
164                }
165           }
166 
167           // To get the deleting node's next node
168           Node *pNextNode = pNode->pNext;
169 
170           // If pNextNode is NULL, it means the deleting node is the tail node, we should change the m_pTail pointer
171           if (pNextNode == NULL)
172           {
173                m_pTail = pPreviousNode;
174           }
175 
176           // Relink the list
177           pPreviousNode->pNext = pNextNode;
178      }
179 
180      // Delete the node
181      delete pNode;
182      pNode = NULL;
183      --m_lCount;
184 }
185 
186 void JTList::RemoveAll()
187 {
188      delete this;
189 }
190 
191 void JTListIterator::First()
192 {
193      m_pCurrent = m_pJTList->First();
194 }
195 
196 void JTListIterator::Next()
197 {
198      m_pCurrent = m_pCurrent->pNext;
199 }
200 
201 bool JTListIterator::IsDone() const
202 {
203      return m_pCurrent == m_pJTList->Last()->pNext;
204 }
205 
206 Node *JTListIterator::CurrentItem() const
207 {
208      return m_pCurrent;
209 }
210 
211 int main()
212 {
213      JTList *pJTList = new JTList;
214      pJTList->Append(10);
215      pJTList->Append(20);
216      pJTList->Append(30);
217      pJTList->Append(40);
218      pJTList->Append(50);
219      pJTList->Append(60);
220      pJTList->Append(70);
221      pJTList->Append(80);
222      pJTList->Append(90);
223      pJTList->Append(100);
224 
225      Iterator *pIterator = new JTListIterator(pJTList);
226 
227      // Print the list by JTListIterator
228      for (pIterator->First(); !pIterator->IsDone(); pIterator->Next())
229      {
230           cout<<pIterator->CurrentItem()->value<<"->";
231      }
232      cout<<"NULL"<<endl;
233 
234      // Test for removing
235      Node *pDeleteNode = NULL;
236      for (pIterator->First(); !pIterator->IsDone(); pIterator->Next())
237      {
238           pDeleteNode = pIterator->CurrentItem();
239           if (pDeleteNode->value == 100)
240           {
241                pJTList->Remove(pDeleteNode);
242                break;
243           }
244      }
245 
246      // Print the list by JTListIterator
247      for (pIterator->First(); !pIterator->IsDone(); pIterator->Next())
248      {
249           cout<<pIterator->CurrentItem()->value<<"->";
250      }
251      cout<<"NULL"<<endl;
252 
253      delete pIterator;
254      delete pJTList;
255 
256      return 0;
257 }

代碼中實現了一個單向鏈表,將鏈表與迭代器解耦。對於多態迭代,添加抽象類AbstractJTList,聲明如下:

class AbstractJTList
{
public:
     virtual Iterator *GetIterator() const = 0;
};
類JTList繼承該抽象類,並實現GetIterator,如下:
 
Iterator *JTList::GetIterator() const
{
     return new JTListIterator(this);
}

好了,這樣的話,在客戶端就不用去new JTListIterator了,只需要這樣:

Iterator *pIterator = pJTList->GetIterator();

這就完全好了;但是,這樣又出現另外一個問題,我在GetIterator中new了一個JTListIterator,對於客戶端來說,我並不知道這個new操作的存在,就會出現客戶端不會去釋放這個new開辟的內存,那么如何實現這個內存的自動釋放呢。好了,就結合迭代器模式,再將之前總結的RAII機制再實際運用一次。

根據RAII機制,需要將這個迭代器進行封裝,讓它具有自動釋放的功能,就得借助另一個類,如下:

class IteratorPtr
{
public:
     IteratorPtr(Iterator *pIterator) : m_pIterator(pIterator){}
     ~IteratorPtr() { delete m_pIterator; }

     Iterator *operator->(){ return m_pIterator; }
     Iterator &operator*() { return *m_pIterator; }

private:
     IteratorPtr(const IteratorPtr &);
     IteratorPtr &operator=(const IteratorPtr &);
     void *operator new(size_t size);
     void operator delete(void *);

private:
     Iterator *m_pIterator;
};

我們在使用的時候,就像下面這樣:

IteratorPtr pIterator(pJTList->GetIterator());

這樣就省去了釋放迭代器的麻煩了。

 

總結

迭代器模式是一個很經典的模式。但是,就是因為它太經典了,如果每次都要程序員去重復造輪子,就有點說不過去了,所以,現在基本成型的類庫,都非常好的實現了迭代器模式,在使用這些類庫提供的容器時,並不需要我們親自去實現對應的迭代器;就好比STL了。但是話又說回來了,如此經典的東西,你不去學習是不是很可惜啊;是吧,在當今社會,技多不壓身。好了,永遠記住,設計模式是一種思想,並不是一層不變的,一種思想,你懂的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM