当前位置: 首页 > backend >正文

迭代器设计模式

迭代器设计模式是一种行为模式,它提供了一种标准方法来按顺序访问集合的元素,而无需公开其内部结构。

它在以下情况下特别有用:
您需要 以一致且灵活的方式遍历集合(如列表、树或图形)。
您希望支持多种迭代方式(例如,向前、向后、过滤或跳过元素)。
您希望将遍历逻辑与集合结构分离,以便客户端不依赖于内部表示。

当面临这种需求时,开发人员通常会编写自定义 循环或直接公开底层数据结构(如 或 )。例如,类 可能会公开其 数组,并让客户端根据需要进行迭代。forArrayListLinkedListPlaylistsongs

但这种方法使客户端与集合的内部结构紧密耦合,并且违反了封装。如果内部存储发生更改,客户端代码将中断。添加新的遍历逻辑或支持延迟迭代也变得困难。

迭代器模式通过将迭代逻辑抽象到一个专用对象(迭代器)中来解决这个问题。集合通过类似 的方法提供迭代器,客户端使用它来逐个访问元素。 createIterator()

这使得更改集合的结构、添加新的遍历样式以及保留干净、封装的设计变得容易。

让我们通过一个真实世界的示例来了解如何应用迭代器模式来构建一种更易于维护、可扩展和标准化的方法来遍历集合。

问题:遍历播放列表
想象一下,您正在构建一个允许用户创建和管理播放列表的音乐流媒体应用程序。每个播放列表都存储一个歌曲列表并提供以下功能:

一首一首地播放歌曲
跳到下一首或上一首歌曲
随机播放歌曲
显示当前歌曲队列

假设您从一个简单的实现开始:

import java.util.List;
import java.util.ArrayList;public class Playlist implements IterableCollection<String> {private final List<String> songs = new ArrayList<>();public void addSong(String song) {songs.add(song);}public String getSongAt(int index) {return songs.get(index);}public int getSize() {return songs.size();}@Overridepublic Iterator<String> createIterator() {return new PlaylistIterator(this);}
}

客户端代码可能如下所示:

public class MusicPlayer {public static void main(String[] args) {Playlist playlist = new Playlist();playlist.addSong("Shape of You");playlist.addSong("Bohemian Rhapsody");playlist.addSong("Blinding Lights");Iterator<String> iterator = playlist.createIterator();System.out.println("Now Playing:");while (iterator.hasNext()) {System.out.println("🎵 " + iterator.next());}}
}

为什么这是一个问题

  1. 1. 破坏封装
    通过公开歌曲的内部列表 (),您可以允许客户端直接修改集合。这可能会导致意想不到的副作用,例如从列表中删除歌曲、重新排序歌曲或注入空值。getSongs()
  2. 2. 将客户端与集合类型紧密耦合
    客户端假定播放列表使用 .如果您决定更改内部存储(例如,更改为 、 数组,甚至是流式缓冲区),则客户端代码会中断。ListLinkedList
  3. 3. 有限的遍历选项
    客户端停留在默认的迭代顺序上。支持多种遍历样式(例如,反向、随机、过滤)每次都需要重写循环逻辑——这违反了单一责任和开放/关闭原则。
  4. 4. 重复和刚性
    随着更多功能的添加(例如,预览歌曲、仅播放收藏夹),遍历歌曲的逻辑会在多个类之间重复。

我们真正需要什么
我们需要一种干净、标准化的方式来:

遍历播放列表中的歌曲而不公开内部收藏
允许不同的遍历样式(前进、后退、随机播放)
从数据结构本身抽象出迭代逻辑
保留封装并允许播放列表在内部更改,而不会影响客户端代码
这正是迭代器模式的用武之地。

迭代器模式
迭代器模式提供了一种标准方法来遍历集合中的元素,而无需暴露其内部结构。

该集合不是让客户端访问底层数据(如 ),而是提供了一个迭代器对象,该对象通过公共接口(例如 , )提供对其元素的顺序访问 。ArrayListhasNext()next()

类图

 

  1. 1. 迭代器(接口)
    定义遍历集合的协定。
    声明遍历元素的方法,例如:
    hasNext()— 检查是否有更多元素
    next()— 返回序列中的下一个元素
  2. 2. ConcreteIterator
    实现 特定集合的接口。 Iterator
    维护集合中的当前位置,并一次循环访问一个项目。
  3. 3. IterableCollection(接口)
    定义创建迭代器的方法。
    将集合的结构与遍历逻辑分开
  4. 4. 混凝土收藏
    存储实际数据并实现 接口。IterableCollection
    它将遍历逻辑委托给迭代器。
    返回迭代器以遍历集合项

实现迭代器
现在让我们实现迭代器模式,将我们在播放列表中遍历歌曲的方式与播放列表的内部结构方式解耦。

我们将构建:
存储歌曲的 A Playlist
可以在 播放列表中移动的PlaylistIterator
使用迭代器的客户端,无需知道播放列表如何存储其歌曲

  1. 1. 定义 接口Iterator
    此接口定义循环访问集合所需的标准作。
public interface Iterator<T> {boolean hasNext();T next();
}
  1. 2. 定义 接口IterableCollection
    这表示可以生成迭代器的任何集合。
public interface IterableCollection<T> {Iterator<T> createIterator();
}
  1. 3. 实施具体收集 –Playlist
    该类将保存歌曲列表并返回一个迭代器来遍历它们。
import java.util.List;
import java.util.ArrayList;public class Playlist implements IterableCollection<String> {private final List<String> songs = new ArrayList<>();public void addSong(String song) {songs.add(song);}public String getSongAt(int index) {return songs.get(index);}public int getSize() {return songs.size();}@Overridepublic Iterator<String> createIterator() {return new PlaylistIterator(this);}
}
  1. 4. 实现具体迭代器 –PlaylistIterator
    本课程知道如何 一次遍历一首歌曲。Playlist
public class PlaylistIterator implements Iterator<String> {private final Playlist playlist;private int index = 0;public PlaylistIterator(Playlist playlist) {this.playlist = playlist;}@Overridepublic boolean hasNext() {return index < playlist.getSize();}@Overridepublic String next() {return playlist.getSongAt(index++);}
}
  1. 5. 客户端代码 – 使用迭代器
    客户端现在可以循环访问播放列表,而无需知道它在内部是如何实现的。
public class MusicPlayer {public static void main(String[] args) {Playlist playlist = new Playlist();playlist.addSong("Shape of You");playlist.addSong("Bohemian Rhapsody");playlist.addSong("Blinding Lights");Iterator<String> iterator = playlist.createIterator();System.out.println("Now Playing:");while (iterator.hasNext()) {System.out.println("🎵 " + iterator.next());}}
}

输出

Now Playing:
🎵 Shape of You
🎵 Bohemian Rhapsody
🎵 Blinding Lights

我们取得了什么成就
封装:客户端从不直接访问内部列表
一致的遍历接口:所有迭代器都遵循相同的 / 模式hasNext()next()
开放/关闭原则:我们可以在不更改播放列表或播放器的情况下添加新的迭代器(反向、随机播放)

灵活的架构:适用于不同的集合类型,而不仅仅是列表
可重用逻辑:迭代器可以在任何需要遍历播放列表的上下文中使用

其他阅读材料:

https://pan.baidu.com/s/1c1oQItiA7nZxz8Rnl3STpw?pwd=yftc
https://pan.quark.cn/s/dec9e4868381

http://www.xdnf.cn/news/18723.html

相关文章:

  • 《XXL-Job 全面介绍:Java 开发中的分布式任务调度框架》
  • 【互动屏幕】为什么现在数字展厅偏爱地面互动装置?
  • 嵌入式Linux内核编译与配置
  • 神经网络与梯度算法:深度学习的底层逻辑与实战解析
  • 微论-神经网络中记忆的演变
  • “Datawhale AI夏令营--coze空间
  • Java 探针的原理
  • 深入解析:为什么应该避免使用 atoi、atol 和 atof 函数
  • 《C++ Primer 第五版》省略符号(...)
  • 【小增长电商技术分享】电商支付宝批量转账工具技术测评:架构特性、合规风险与选型方法论,支付宝官方|小增长|云方付|易推客省心返
  • vi/vim 查找字符串
  • Ajax笔记(上)
  • Spark面试题
  • Redis面试精讲 Day 30:Redis面试真题解析与答题技巧
  • 南京魔数团:AR技术引领远程协作新纪元
  • Java网络编程:从入门到精通
  • STM32之DMA详解
  • 算法题记录01:
  • 8月25日
  • 专题:2025人工智能2.0智能体驱动ERP、生成式AI经济现状落地报告|附400+份报告PDF、原数据表汇总下载
  • [论文阅读]RQ-RAG: Learning to Refine Queries for Retrieval Augmented Generation
  • k8s的etcd备份脚本
  • AR技术赋能农业机械智能运维
  • 电机控制::基于编码器的速度计算与滤波::RLS
  • 【C++】第二十六节—C++11(中) | 右值引用和移动语义(续集)+lambda
  • Linux_用 `ps` 按进程名过滤线程,以及用 `pkill` 按进程名安全杀进程
  • 机器学习-大语言模型Finetuning vs. Prompting
  • 大型语言模型基准测试综述《A Survey on Large Language Model Benchmarks.pdf》核心内容总结
  • 京东前端社招面经
  • 多维度指标交叉计算查询方案