1. 程式人生 > >C++設計模式之迭代器模式

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

提供一種方法順序訪問一個聚合物件中各個元素,而又不暴露該物件的內部表示。當你需要訪問一個聚集物件,而且不管這些物件是什麼都需要遍 歷的時候,就應該考慮用迭代器模式。同時需要對聚集有多種方式遍歷時,可以考慮用迭代器模式。為遍歷不同的聚集結構提供如開始、下一個、是否結束、當前哪 一項等統一介面。迭代器模式就是分離了集合物件的遍歷行為,抽象出一個迭代器類來負責,這樣既可以做到不暴露集合的內部結構,又可讓外部程式碼透明地訪問集 合內部的資料。

程式碼如下:

複製程式碼
#include <iostream>
#include <string>
#include 
<vector> using namespace std; class Iterator { public: Iterator(){}; virtual ~Iterator(){}; virtual string First() = 0; virtual string Next() = 0; virtual string GetCur() = 0; virtual bool IsEnd() = 0; }; class Aggregate { public: virtual int Count() = 0; virtual
void Push(const string& strValue)=0; virtual string Pop(const int nIndex)=0; virtual Iterator* CreateIterator() = 0; }; class ConcreteIterator : public Iterator { public: ConcreteIterator(Aggregate* pAggregate):m_nCurrent(0),Iterator() { m_Aggregate = pAggregate; }
string First() { return m_Aggregate->Pop(0); } string Next() { string strRet; m_nCurrent++; if(m_nCurrent < m_Aggregate->Count()) { strRet = m_Aggregate->Pop(m_nCurrent); } return strRet; } string GetCur() { return m_Aggregate->Pop(m_nCurrent); } bool IsEnd() { return ((m_nCurrent >= m_Aggregate->Count()) ? true: false); } private: Aggregate* m_Aggregate; int m_nCurrent; }; class ConcreteAggregate : public Aggregate { public: ConcreteAggregate():m_pIterator(NULL) { m_vecItems.clear(); } ~ConcreteAggregate() { if(NULL != m_pIterator) { delete m_pIterator; m_pIterator = NULL; } } Iterator* CreateIterator() { if(NULL == m_pIterator) { m_pIterator = new ConcreteIterator(this); } return m_pIterator; } int Count() { return m_vecItems.size(); } void Push(const string& strValue) { m_vecItems.push_back(strValue); } string Pop(const int nIndex) { string strRet; if(nIndex < Count()) { strRet = m_vecItems[nIndex]; } return strRet; } private: vector<string> m_vecItems; Iterator* m_pIterator; };
複製程式碼

main 函式

複製程式碼
#include "iterator.h"

int main()
{
    ConcreteAggregate* pName = NULL;
    pName = new ConcreteAggregate();
    if(NULL != pName)
    {
        pName->Push("hello");
        pName->Push("word");
        pName->Push("cxue");
    }
    Iterator* iter = NULL;
    iter = pName->CreateIterator();
    if(NULL != iter)
    {
        string strItem = iter->First();
        while(!iter->IsEnd())
        {
            cout << iter->GetCur() << " is ok" << endl;
            iter->Next();
        }
    }
    system("pause");

    return 0;
}
複製程式碼

======================================================================================

前言

又到年底了,時間真的過的好快啊。最近也非常感傷,總是懷念大學的日子,做夢的時候也常常夢到。夢到大學在電腦前傻傻的敲著鍵盤,寫著程式碼,對付著資料結構與演算法的作業;建立一個連結串列,遍歷連結串列,列印連結串列。現在把那個時候宣告的連結串列的標頭檔案拿出來看看:

typedefstruct tagNode
{int value;
     tagNode *pPre;
     tagNode *pNext;}Node;classCList{public:CList();CList(size_t n);~CList();boolPushBack(int value);boolPopBack(int&value);boolInsert(int pos,int value);boolDelete(int pos);boolIsEmpty();intGetLength();voidPrint();// To iterate the listboolHasNext();intNext();private:int m_iLength;Node*m_pCurrent;Node*m_pHead;Node*m_pTail;};

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

迭代器模式

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

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

UML類圖

果凍想 | 一個原創文章分享網站

Iterator:定義迭代器訪問和遍歷元素的介面;
ConcreteIterator:實現具體的迭代器;
Aggregate:定義的容器,建立相應迭代器物件的介面;
ConcreteAggregate:具體的容器實現建立相應迭代器的介面,該操作返回ConcreteIterator的一個適當的例項。

使用場合

  1. 訪問一個聚合物件的內容而無需暴露它的內部表示;
  2. 支援對聚合物件的多種遍歷(從前到後,從後到前);
  3. 為遍歷不同的聚合結構提供一個統一的介面,即支援多型迭代。

作用

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

程式碼實現

#include<iostream>usingnamespace std;typedefstruct tagNode
{int value;
     tagNode *pNext;}Node;classJTList{public:JTList(): m_pHead(NULL), m_pTail(NULL){};JTList(constJTList&);~JTList();JTList&operator=(constJTList&);longGetCount()const;Node*Get(constlong index)const;Node*First()const;Node*Last()const;boolIncludes(constint&)const;voidAppend(constint&);voidRemove(Node*pNode);voidRemoveAll();private:Node*m_pHead;Node*m_pTail;long m_lCount;};classIterator{public:virtualvoidFirst()=0;virtualvoidNext()=0;virtualboolIsDone()const=0;virtualNode*CurrentItem()const=0;};classJTListIterator:publicIterator{public:JTListIterator(JTList*pList): m_pJTList(pList), m_pCurrent(NULL){}virtualvoidFirst();virtualvoidNext();virtualboolIsDone()const;virtualNode*CurrentItem()const;private:JTList*m_pJTList;Node*m_pCurrent;};JTList::~JTList(){Node*pCurrent = m_pHead;Node*pNextNode = NULL;while(pCurrent){
          pNextNode = pCurrent->pNext;delete pCurrent;
          pCurrent = pNextNode;}}longJTList::GetCount()const{return m_lCount;}Node*JTList::Get(constlong index)const{// The min index is 0, max index is count - 1if(index > m_lCount -1|| index <0){return NULL;}int iPosTemp =0;Node*pNodeTemp = m_pHead;while(pNodeTemp){if(index == iPosTemp++){return pNodeTemp;}
          pNodeTemp = pNodeTemp->pNext;}return NULL;}Node*JTList::First()const{return m_pHead;}Node*JTList::Last()const{return m_pTail;}boolJTList::Includes(constint&value)const{Node*pNodeTemp = m_pHead;while(pNodeTemp){if(value == pNodeTemp->value){returntrue;}
          pNodeTemp = pNodeTemp->pNext;}returnfalse;}voidJTList::Append(constint&value){// Create the new nodeNode*pInsertNode =newNode;
     pInsertNode->value = value;
     pInsertNode->pNext = NULL;// This list is emptyif(m_pHead == NULL){
          m_pHead = m_pTail = pInsertNode;}else{
          m_pTail->pNext = pInsertNode;
          m_pTail = pInsertNode;}++m_lCount;}voidJTList::Remove(Node*pNode){if(pNode == NULL || m_pHead == NULL || m_pTail == NULL){return;}if(pNode == m_pHead)// If the deleting node is head node{Node*pNewHead = m_pHead->pNext;
          m_pHead = pNewHead;}else{// To get the deleting node's previous nodeNode*pPreviousNode = NULL;Node*pCurrentNode <