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

概述

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

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

基本原理

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

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

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

#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;
}

总结

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/75735.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JVM 垃圾回收器是如何判断一个对象是否要回收?

JVM 垃圾回收器&#xff08;Garbage Collector&#xff09;需要判断哪些对象是“垃圾”&#xff0c;即不再被程序使用的对象&#xff0c;以便回收它们占用的内存。JVM 主要使用以下两种方法来判断对象是否是垃圾&#xff1a; 1. 引用计数算法 (Reference Counting): 原理&…

kali——httrack

目录 前言 使用教程 前言 HTTrack 是一款运行于 Kali Linux 系统中的开源网站镜像工具&#xff0c;它能将网站的页面、图片、链接等资源完整地下载到本地&#xff0c;构建出一个和原网站结构相似的离线副本。 使用教程 apt install httrack //安装httrack工具 httrac…

kotlin函数类型

一 函数类型定义 1 定义 函数类型就是 (Int, Int) -> Int 函数类型其实就是将函数的 “参数类型” 和 “返回值类型” 抽象出来 2 示例 &#xff1a; (Int, Int) -> Int 表示接收两个 Int 参数并返回 Int 的函数类型&#xff1b; (String) -> Unit 表示接收 Strin…

C# Winform 入门(9)之如何封装并调用dll

封装dll 首先创建 .Net平台 类库 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace _09.Encapsulation_dll {public class Program{/// <summary>/// 求两个double类型的数值的和/// &l…

前后端分离下,Spring Boot 请求从发起到响应的完整执行流程

以下是前后端分离架构下&#xff0c;Spring Boot 请求从发起到响应的完整执行流程&#xff0c;结合你提出的所有问题&#xff0c;按真实执行顺序和职责链条重新整理所有核心概念、结构、关键类、数据转换点和典型代码示例&#xff1a; 一、前端发起请求&#xff08;步骤1-2&…

基于sklearn实现文本摘要思考

和各位小伙伴分享一下使用sklearn进行文本摘要的思考。 第一版本 原理 提取式文本摘要的基本原理是&#xff1a; 将文本分割成句子 计算每个句子的重要性(权重) 选择权重最高的几个句子组成摘要 常用的句子权重计算方法&#xff1a; TF-IDF&#xff1a;基于词频-逆文档频…

OpenHarmony子系统开发 - DFX(三)

OpenHarmony子系统开发 - DFX&#xff08;三&#xff09; 五、HiTraceMeter开发指导 HiTraceMeter概述 简介 HiTraceMeter在OpenHarmony中&#xff0c;为开发者提供业务流程调用链跟踪的维测接口。通过使用该接口所提供的功能&#xff0c;可以帮助开发者迅速获取指定业务流…

2025年 能够有效提升AI的生成质量和逻辑严谨性 的通用型系统提示

以下是三个经过精心设计的通用型系统提示&#xff08;System Prompt&#xff09;&#xff0c;能够有效提升AI的生成质量和逻辑严谨性&#xff0c;适用于各类对话、分析和创作场景&#xff1a; Prompt 1 - 专家级分步验证模式 你是一个具备跨领域知识整合能力的超级AI&#xff…

python爬虫:小程序逆向实战教程

根据我之前发表的文章&#xff0c;我们进行延伸实战https://blog.csdn.net/weixin_64809364/article/details/146981598?spm1001.2014.3001.5501 1. 想要爬取什么小程序&#xff0c;我们进行搜索 2. 找到我们vx小程序的文件地址&#xff0c;我们就可以进行破解 破解步骤强看…

C语言变长数组(VLA)详解:灵活处理动态数据的利器

引言 在C语言中&#xff0c;传统的数组大小必须在编译时确定&#xff0c;这限制了程序处理动态数据的灵活性。C99标准引入的变长数组&#xff08;Variable-Length Array, VLA&#xff09; 打破了这一限制&#xff0c;允许数组长度在运行时动态确定。本文将深入解析VLA的语法、…

串口数据转换为IP数据

串口数据转换为IP数据是一种常见的通信技术,用于将传统的串行设备(如传感器、控制器等)接入现代的IP网络。以下是详细介绍: 1. 转换原理 串口数据转换为IP数据的过程涉及硬件和软件的结合,核心是将串行数据封装为TCP/IP或UDP/IP数据包,通过网络传输。具体步骤如下: 硬…

client-go如何监听自定义资源

如何使用 client-go 监听自定义资源 在 Kubernetes 中使用 client-go 监听自定义资源&#xff08;Custom Resource&#xff0c;简称 CR&#xff09;需要借助 Dynamic Client 或 Custom Informer&#xff0c;因为 client-go 的标准 Clientset 只支持内置资源&#xff08;如 Pod…

C++软件开发架构

文章目录 1.全局消息通信MsgHandler.h单元测试(QTest)MsgHandlerUnitTest.hMsgHandlerUnitTest.cpp 2.实例间通信InstMsgHandler.h单元测试InstMsgHandlerUnitTest.hInstMsgHandlerUnitTest.cpp 1.全局消息通信 1. 适用于类与类单个对象实例之间的通信&#xff0c;多个对象需要…

AI Agent设计模式一:Chain

概念 &#xff1a;线性任务流设计 ✅ 优点&#xff1a;逻辑清晰易调试&#xff0c;适合线性处理流程❌ 缺点&#xff1a;缺乏动态分支能力 from typing import TypedDictfrom langgraph.graph import StateGraph, END# 定义后续用到的一些变量 class CustomState(TypedDict):p…

Git三剑客:工作区、暂存区、版本库深度解析

一、引言&#xff1a;为什么需要理解Git的核心区域&#xff1f; 作为开发者&#xff0c;Git是日常必备的版本控制工具。但你是否曾因以下问题感到困惑&#xff1f; 修改了文件&#xff0c;但 git status 显示一片混乱&#xff1f; git add 和 git commit 到底做了什么&#x…

Python数据类型-list

列表(List)是Python中最常用的数据类型之一&#xff0c;它是一个有序、可变的元素集合。 1. 列表基础 创建列表 empty_list [] # 空列表 numbers [1, 2, 3, 4, 5] # 数字列表 fruits [apple, banana, orange] # 字符串列表 mixed [1, hello, 3.14, True] # 混合类型…

Keepalive+LVS+Nginx+NFS高可用项目

项目架构 分析 主机规划 主机系统安装应用网络IPclientredhat 9.5无NAT172.25.250.115/24lvs-masterrocky 9.5ipvsadm&#xff0c;keepalivedNAT172.25.250.116/24 VIP 172.25.250.100/32lvs-backuprocky 9.5ipvsadm&#xff0c;keepalivedNAT172.25.250.117/24 VIP 172.25.2…

【视觉与语言模型参数解耦】为什么?方案?

一些无编码器的MLLMs统一架构如Fuyu&#xff0c;直接在LLM内处理原始像素&#xff0c;消除了对外部视觉模型的依赖。但是面临视觉与语言模态冲突的挑战&#xff0c;导致训练不稳定和灾难性遗忘等问题。解决方案则是通过参数解耦方法解决模态冲突。 在多模态大语言模型&#xf…

AI比人脑更强,因为被植入思维模型【43】蝴蝶效应思维模型

giszz的理解&#xff1a;蝴蝶效应我们都熟知&#xff0c;就是说一个微小的变化&#xff0c;能带动整个系统甚至系统的空间和时间的远端&#xff0c;产生巨大的链式反应。我学习后的启迪&#xff0c;简单的说&#xff0c;就是不要忽视任何微小的问题&#xff0c;更多时候&#x…

AI 数理逻辑基础之统计学基本原理(上)

目录 文章目录 目录统计学统计学基本概念描述性统计数据可视化图表工具 汇总统计统计数据的分布情况&#xff1a;中位数、众数、平均值统计数据的离散程度&#xff1a;极差、方差、标准差、离散系数 相关分析Pearson 线性关系相关系数Spearman 单调关系相关系数 回归分析回归模…