迭代器模式
在GOF的《设计模式:可复用面向对象软件的基础》一书中对迭代器模式是这样说的:提供一种方法顺序访问一个聚合对象中各个元素,而又不需要暴露该对象的内部表示。
一个聚合对象,就是所谓的对象容器了;作为一个容器,都应该提供一种方法来让别人可以访问它的元素;但是,有的时候,我是不希望遍历容器的人知道我的容器是如何实现的;那该怎么办?就像我在大学那样实现的链表,只提供了从头到尾的遍历,如果我需要从尾到头的遍历呢?是不是我又要添加对应的方法了呢!!!容器的遍历方式千变万化,我们不知道需求是如何的,如果需求变了,那么我们的代码就会发生很大的改动,所以,我们需要去改变;对于上面的代码,当我对同一个链表对象进行多次遍历时,是不是就出现了m_pCurrent对象混乱的局面呢?是的,这一切的一切,都说明,我们必须去将一个容器的内部结构与它的遍历进行解耦,要是出现上面的情况时,我们就无法面对。就好比STL中的容器,它将容器中对象的实现和遍历很好的解耦了,所以,我们就无法知道它的内部是如何组织对象数据的,同时,我们也可以按照我们自己的想法去遍历容器,而不会出现任何差错。在我们的项目中使用迭代器模式就能很好的将容器对象的内部表示与对它的遍历进行解耦。接下来,我们再来详细的总结迭代器模式。
UML类图
Iterator:定义迭代器访问和遍历元素的接口;
ConcreteIterator:实现具体的迭代器;
Aggregate:定义的容器,创建相应迭代器对象的接口;
ConcreteAggregate:具体的容器实现创建相应迭代器的接口,该操作返回ConcreteIterator的一个适当的实例。
使用场合
- 访问一个聚合对象的内容而无需暴露它的内部表示;
- 支持对聚合对象的多种遍历(从前到后,从后到前);
- 为遍历不同的聚合结构提供一个统一的接口,即支持多态迭代。
作用
- 它支持以不同的方式遍历一个聚合,甚至都可以自己定义迭代器的子类以支持新的遍历;
- 迭代器简化了聚合的接口,有了迭代器的遍历接口,聚合本身就不再需要类似的遍历接口了。这样就简化了聚合的接口;
- 在同一个聚合上可以有多个遍历,每个迭代器保持它自己的遍历状态;因此,我们可以同时进行多个遍历。
代码实现
#include<iostream> using namespace std; typedef struct tagNode{ int value; tagNode* pNext; }Node; class JTList //定义的容器,先自己实现 { private: Node* m_pHead, *m_pTail; public: JTList() :m_pHead(nullptr), m_pTail(nullptr){} 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: long m_lCount; }; JTList::~JTList() { Node* pCurrent = m_pHead; Node* pNextNode = nullptr; while (pCurrent) { pNextNode = pCurrent->pNext; delete pCurrent; pCurrent = pNextNode; } } long JTList::getCount()const { return m_lCount; } Node* JTList::get(const long index)const { if (index > m_lCount - 1 || index < 0) { return nullptr; } int iPosTemp = 0; Node* pNodeTemp = m_pHead; while (pNodeTemp) { if (index==iPosTemp++) { return pNodeTemp; } pNodeTemp = pNodeTemp->pNext; } return nullptr; } 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 (pNodeTemp->value==value) { return true; } pNodeTemp = pNodeTemp->pNext; } return false; } void JTList::Append(const int& value) { Node* pInsertNode = new Node; pInsertNode->value = value; pInsertNode->pNext = nullptr; if (m_pHead==nullptr) { m_pHead=m_pTail = pInsertNode; } else //在尾部插入元素 { m_pTail->pNext = pInsertNode; m_pTail = pInsertNode; } ++m_lCount; } void JTList::Remove(Node* pNode) { if (pNode==nullptr||m_pHead==nullptr||m_pTail==nullptr) { return; } if (m_pHead==pNode) { Node* pNewNode = m_pHead->pNext; //记下头节点后面的元素记住 m_pHead = pNewNode; } else { //To get the deleting node's previous node Node* m_previousNode = nullptr; Node* pCurrentNode = m_pHead; while (pCurrentNode) { if (pCurrentNode==pNode) { break; } m_previousNode = pCurrentNode; pCurrentNode = pCurrentNode->pNext; } //To get the deleting node's next node Node* m_nextNode = pCurrentNode->pNext; //if m_nextNode is null, it means the deleting node is the tail node, we should change the m_pTail pointer if (m_nextNode==nullptr) { m_pTail = m_previousNode; } m_previousNode->pNext = m_nextNode; } delete pNode; pNode = nullptr; --m_lCount; } void JTList::RemoveAll() { delete this; } 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 { private: JTList* m_pJTList; Node* m_pCurrent; public: JTListIterator(JTList* pList) :m_pJTList(pList), m_pCurrent(nullptr){} virtual void first(); virtual void next(); virtual bool isDone()const; Node *currentItem()const; }; 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); for (pIterator->first(); !pIterator->isDone();pIterator->next()) { cout << pIterator->currentItem()->value << "-->"; } cout << "NULL" << endl; Node* pDeleteNode = nullptr; for (pIterator->first(); !pIterator->isDone();pIterator->next()) { pDeleteNode = pIterator->currentItem(); if (pDeleteNode->value==100) { pJTList->Remove(pDeleteNode); break; } } for (pIterator->first(); !pIterator->isDone();pIterator->next()) { cout << pIterator->currentItem()->value << "-->"; } cout <<"NULL" << endl; delete pJTList; delete pIterator; return 0; }
总结
迭代器模式是一个很经典的模式。但是,就是因为它太经典了,如果每次都要程序员去重复造轮子,就有点说不过去了,所以,现在基本成型的类库,都非常好的实现了迭代器模式,在使用这些类库提供的容器时,并不需要我们亲自去实现对应的迭代器;就好比STL了。但是话又说回来了,如此经典的东西,你不去学习是不是很可惜啊;是吧,在当今社会,技多不压身。好了,永远记住,设计模式是一种思想,并不是一层不变的,一种思想,你懂的。