实战设计模式之迭代器模式

概述

与上一篇介绍的解释器模式一样,迭代器模式也是一种行为设计模式。它提供了一种方法来顺序访问一个聚合对象中的各个元素,而无需暴露该对象的内部表示。简而言之,迭代器模式允许我们遍历集合数据结构中的元素,而不必了解这些集合的底层实现细节。

音乐播放器是运用迭代器模式的一个典型例子:当我们使用音乐播放器听歌时,通常会有"下一首"、"上一首"的功能来切换歌曲。这里的歌曲播放列表就相当于一个聚合对象,而用于切换歌曲的功能就是迭代器。用户可以轻松地遍历整个播放列表,而无需了解歌曲是如何存储或排序的。

基本原理

迭代器模式的核心思想在于:提供一种相对"透明"的方法,以便顺序访问聚合对象中的每一个元素。通过这种方法,迭代器模式实现了遍历逻辑与数据结构之间的解耦,使得不同的聚合对象可以使用相同的遍历逻辑。另外,迭代器模式还支持多种元素遍历的策略。

迭代器模式主要由以下四个核心组件构成。

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,添加了几首歌曲,并创建了对应的迭代器来执行播放操作。

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

总结

迭代器模式将遍历集合的逻辑从聚合对象中分离出来,使得聚合对象不需要关心如何遍历其内部的数据结构。这增强了代码的封装性,减少了模块间的耦合度。由于遍历逻辑被移到了迭代器中,聚合对象的接口变得更加简洁明了,只需要提供创建迭代器的方法即可,而无需暴露复杂的遍历逻辑或内部实现细节。另外,同一聚合对象可以通过不同的迭代器实现不同的遍历方式(比如:顺序遍历、逆序遍历等),增加了系统的灵活性和适应性。

但引入迭代器模式可能会导致系统复杂度上升,尤其是在处理简单集合时,使用迭代器可能显得过于繁琐,增加了不必要的间接层。在某些情况下,尤其是当集合非常庞大时,创建多个迭代器实例可能会消耗较多的内存资源。特别要注意的是:如果迭代器在遍历时,修改了集合的内容,可能会引发一致性问题。

相关推荐
01空间2 小时前
设计模式简述(九)命令模式
设计模式
01空间3 小时前
设计模式简述(十一)装饰器模式
设计模式
辰辰大美女呀3 小时前
C 语言高级编程指南:回调函数与设计模式
c语言·开发语言·设计模式
01空间3 小时前
设计模式简述(六)代理模式
设计模式
cijiancao5 小时前
23种设计模式中的观察者模式
观察者模式·设计模式
前期后期8 小时前
设计模式:为什么使用模板设计模式(不相同的步骤进行抽取,使用不同的子类实现)减少重复代码,让代码更好维护。
java·前端·设计模式
01空间8 小时前
设计模式简述(八)中介者模式
设计模式
信徒_9 小时前
Spring 中有哪些设计模式?
java·spring·设计模式
01空间11 小时前
设计模式简述(十二)策略模式
设计模式