C++设计模式之迭代器模式

来源:互联网 发布:ipad cellular 知乎 编辑:程序博客网 时间:2024/06/06 05:26

提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。当你需要访问一个聚集对象,而且不管这些对象是什么都需要遍 历的时候,就应该考虑用迭代器模式。同时需要对聚集有多种方式遍历时,可以考虑用迭代器模式。为遍历不同的聚集结构提供如开始、下一个、是否结束、当前哪 一项等统一接口。迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集 合内部的数据。

代码如下:

复制代码
#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;}
复制代码

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

前言

又到年底了,时间真的过的好快啊。最近也非常感伤,总是怀念大学的日子,做梦的时候也常常梦到。梦到大学在电脑前傻傻的敲着键盘,写着代码,对付着数据结构与算法的作业;建立一个链表,遍历链表,打印链表。现在把那个时候声明的链表的头文件拿出来看看:

typedef struct tagNode{     int value;     tagNode *pPre;     tagNode *pNext;}Node;class CList{public:     CList();     CList(size_t n);     ~CList();     bool PushBack(int value);     bool PopBack(int &value);     bool Insert(int pos, int value);     bool Delete(int pos);     bool IsEmpty();     int GetLength();     void Print();     // To iterate the list     bool HasNext();     int Next();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>using namespace std;typedef struct tagNode{     int value;     tagNode *pNext;}Node;class JTList{public:     JTList() : m_pHead(NULL), m_pTail(NULL){};     JTList(const JTList &);     ~JTList();     JTList &operator=(const JTList &);     long GetCount() const;     Node *Get(const long index) const;     Node *First() const;     Node *Last() const;     bool Includes(const int &) const;     void Append(const int &);     void Remove(Node *pNode);     void RemoveAll();private:     Node *m_pHead;     Node *m_pTail;     long m_lCount;};class Iterator{public:     virtual void First() = 0;     virtual void Next() = 0;     virtual bool IsDone() const = 0;     virtual Node *CurrentItem() const  = 0;};class JTListIterator : public Iterator{public:     JTListIterator(JTList *pList) : m_pJTList(pList), m_pCurrent(NULL){}     virtual void First();     virtual void Next();     virtual bool IsDone() const;     virtual Node *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;     }}long JTList::GetCount()const{     return m_lCount;}Node *JTList::Get(const long index) const{     // The min index is 0, max index is count - 1     if (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;}bool JTList::Includes(const int &value) const{     Node *pNodeTemp = m_pHead;     while (pNodeTemp)     {          if (value == pNodeTemp->value)          {               return true;          }          pNodeTemp = pNodeTemp->pNext;     }     return false;}void JTList::Append(const int &value){     // Create the new node     Node *pInsertNode = new Node;     pInsertNode->value = value;     pInsertNode->pNext = NULL;     // This list is empty     if (m_pHead == NULL)     {          m_pHead = m_pTail = pInsertNode;     }     else     {          m_pTail->pNext = pInsertNode;          m_pTail = pInsertNode;     }     ++m_lCount;}void JTList::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 node          Node *pPreviousNode = NULL;          Node *pCurrentNode = m_pHead;          while (pCurrentNode)          {               pPreviousNode = pCurrentNode;               pCurrentNode = pCurrentNode->pNext;               if (pCurrentNode == pNode)               {                    break;               }          }          // To get the deleting node's next node          Node *pNextNode = pNode->pNext;          // If pNextNode is NULL, it means the deleting node is the tail node, we should change the m_pTail pointer          if (pNextNode == NULL)          {               m_pTail = pPreviousNode;          }          // Relink the list          pPreviousNode->pNext = pNextNode;     }     // Delete the node     delete pNode;     pNode = NULL;     --m_lCount;}void JTList::RemoveAll(){     delete this;}void JTListIterator::First(){     m_pCurrent = m_pJTList->First();}void JTListIterator::Next(){     m_pCurrent = m_pCurrent->pNext;}bool JTListIterator::IsDone() const{     return m_pCurrent == m_pJTList->Last()->pNext;}Node *JTListIterator::CurrentItem() const{     return m_pCurrent;}int main(){     JTList *pJTList = new JTList;     pJTList->Append(10);     pJTList->Append(20);     pJTList->Append(30);     pJTList->Append(40);     pJTList->Append(50);     pJTList->Append(60);     pJTList->Append(70);     pJTList->Append(80);     pJTList->Append(90);     pJTList->Append(100);     Iterator *pIterator = new JTListIterator(pJTList);     // Print the list by JTListIterator     for (pIterator->First(); !pIterator->IsDone(); pIterator->Next())     {          cout<<pIterator->CurrentItem()->value<<"->";     }     cout<<"NULL"<<endl;     // Test for removing     Node *pDeleteNode = NULL;     for (pIterator->First(); !pIterator->IsDone(); pIterator->Next())     {          pDeleteNode = pIterator->CurrentItem();          if (pDeleteNode->value == 100)          {               pJTList->Remove(pDeleteNode);               break;          }     }     // Print the list by JTListIterator     for (pIterator->First(); !pIterator->IsDone(); pIterator->Next())     {          cout<<pIterator->CurrentItem()->value<<"->";     }     cout<<"NULL"<<endl;     delete pIterator;     delete pJTList;     return 0;}

代码中实现了一个单向链表,将链表与迭代器解耦。对于多态迭代,添加抽象类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());

这样就省去了释放迭代器的麻烦了。这里一共涉及了三个DEMO工程,提供完整DEMO工程下载。(工程下载)

总结

迭代器模式是一个很经典的模式。但是,就是因为它太经典了,如果每次都要程序员去重复造轮子,就有点说不过去了,所以,现在基本成型的类库,都非常好的实现了迭代器模式,在使用这些类库提供的容器时,并不需要我们亲自去实现对应的迭代器;就好比STL了。但是话又说回来了,如此经典的东西,你不去学习是不是很可惜啊;是吧,在当今社会,技多不压身。好了,永远记住,设计模式是一种思想,并不是一层不变的,一种思想,你懂的。

2014年1月24日 于大连,东软。

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

概述:

        在现在的电视机中,我们使用[后一个][前一个]按钮可以很方便的换台,当按下[后一个]按钮时,将切换到下一个预置的频道。想象一下在陌生的城市中的旅店中看电视。当改变频道时,重要的不是几频道,而是节目内容。如果对一个频道的节目不感兴趣,那么可以换下一个频道,而不需要知道它是几频道。

        这个其实就是我们迭代器模式的精髓:提供一种方法顺序访问一个聚合对象中各个元素而又不需暴露该对象的内部表示。

类图和实例:


迭代器模式由以下角色组成:

1迭代器角色Iterator):迭代器角色负责定义访问和遍历元素的接口。
2.具体迭代器角色Concrete Iterator):具体迭代器角色要实现迭代器接口,并要记录遍历中的当前位置。
3.集合角色Aggregate):集合角色负责提供创建具体迭代器角色的接口。
4.具体集合角色Concrete Aggregate):具体集合角色实现创建具体迭代器角色的接口——这个具体迭代器角色于该集合的结构相关。

[cpp] view plain copy
  1. #include <iostream>  
  2. #include <vector>  
  3. using namespace std;  
  4.   
  5. template<class Item>  
  6. class Iterator  
  7. {  
  8. public:  
  9.     virtual void first()=0;  
  10.     virtual void next()=0;  
  11.     virtual Item* currentItem()=0;  
  12.     virtual bool isDone()=0;  
  13.     virtual ~Iterator(){}  
  14. };  
  15.   
  16. template<class Item>  
  17. class ConcreteAggregate;  
  18.   
  19. template<class Item>  
  20. class ConcreteIterator : public Iterator <Item>  
  21. {  
  22.     ConcreteAggregate<Item> * aggr;  
  23.     int cur;  
  24. public:  
  25.     ConcreteIterator(ConcreteAggregate<Item>*a):aggr(a),cur(0){}  
  26.     virtual void first()  
  27.     {  
  28.         cur=0;  
  29.     }  
  30.     virtual void next()  
  31.     {  
  32.         if(cur<aggr->getLen())  
  33.             cur++;  
  34.     }  
  35.     virtual Item* currentItem()  
  36.     {  
  37.         if(cur<aggr->getLen())  
  38.             return &(*aggr)[cur];  
  39.         else  
  40.             return NULL;  
  41.     }  
  42.     virtual bool isDone()  
  43.     {  
  44.         return (cur>=aggr->getLen());  
  45.     }  
  46. };  
  47.   
  48. template<class Item>  
  49. class Aggregate  
  50. {  
  51. public:  
  52.     virtual Iterator<Item>* createIterator()=0;  
  53.     virtual ~Aggregate(){}  
  54. };  
  55.   
  56. template<class Item>  
  57. class ConcreteAggregate:public Aggregate<Item>  
  58. {  
  59.     vector<Item >data;  
  60. public:  
  61.     ConcreteAggregate()  
  62.     {  
  63.         data.push_back(1);  
  64.         data.push_back(2);  
  65.         data.push_back(3);  
  66.     }  
  67.     virtual Iterator<Item>* createIterator()  
  68.     {  
  69.         return new ConcreteIterator<Item>(this);  
  70.     }  
  71.     Item& operator[](int index)  
  72.     {  
  73.         return data[index];  
  74.     }  
  75.     int getLen()  
  76.     {  
  77.         return data.size();  
  78.     }  
  79. };  
  80.   
  81. int main()  
  82. {  
  83.     Aggregate<int> * aggr =new ConcreteAggregate<int>();  
  84.     Iterator<int> *it=aggr->createIterator();  
  85.   
  86.     for(it->first();!it->isDone();it->next())  
  87.     {  
  88.         cout<<*(it->currentItem())<<endl;  
  89.     }  
  90.     delete it;  
  91.     delete aggr;  
  92.     return 0;  
  93. }  

实现要点:

1.迭代抽象:访问一个聚合对象的内容而无需暴露它的内部表示。

2.迭代多态:为遍历不同的集合结构提供一个统一的接口,从而支持同样的算法在不同的集合结构上进行操作。

3.迭代器的健壮性考虑:遍历的同时更改迭代器所在的集合结构,会导致问题。

适用性:

1.访问一个聚合对象的内容而无需暴露它的内部表示。 

2.支持对聚合对象的多种遍历。 

3.为遍历不同的聚合结构提供一个统一的接口(支持多态迭代)

其他:

1.在C++下可以参看 STL iterators 的实现。

2.在.NET下实现Iterator模式,对于聚集接口和迭代器接口已经存在了,其中IEnumerator扮演的就是迭代器的角色,而IEnumerable则扮演的就是抽象聚集的角色,她只有一个GetEnumerator()方法,如果集合对象需要具备跌代遍历的功能,就必须实现该接口。

3在Java下可以参看集合类型迭代器(java.util.Iterator,java.util.Enumeration)。


LCL_data原创于CSDN.NET【http://blog.csdn.net/lcl_data/article/details/9310313】


0 0