• 设计模式之迭代器模式


    什么是迭代器模式

            迭代器模式(Iterator pattern)是一种对象行为型设计模式,它提供了一种方法来顺序访问聚合对象中的元素,而又不暴露该对象的内部表示,同时也可以将迭代逻辑与聚合对象的实现分离,增强了代码的可维护性和可扩展性。

            迭代器模式的特征如下:

    • 迭代器模式允许一个聚合对象公开它的每一个元素,而又不暴露其内部表示,即它提供了一种方法来遍历聚合对象中的每一个元素,而不需要了解该对象的内部结构或实现。
    • 迭代器模式的主要目的是将迭代逻辑封装在迭代器对象中,而不是将该逻辑嵌入到聚合对象中。这样可以将迭代逻辑与聚合对象的实现分离,使得它们可以独立地改变和扩展。
    • 迭代器模式是通过定义一个迭代器接口来实现的。该接口提供了访问和遍历聚合对象的元素的方法。聚合对象则需要实现一个能够创建相应迭代器的接口。

    迭代器模式的核心角色

    1. 迭代器接口(Iterator):定义了访问和遍历集合元素的方法,为使用者提供了一种方便的方式来遍历集合中的元素,而不必关心集合的实现细节,如:是否能够继续遍历下一个元素、取出下一个元素等。
    2. 具体迭代器(ConcreteIterator):持有一个具体的集合,并且实现了迭代器接口,实际负责按照特定的顺序遍历集合中的元素,并将遍历结果反馈给使用者。
    3. 集合接口(Aggregate):定义了创建迭代器的抽象方法,隐藏了具体集合的表示和实现,具体的表示和实现由具体集合类来负责实现;
    4. 具体集合(ConcreteAggregate):该类实现了集合接口,创建出具体的迭代器对象,供使用者通过迭代器来访问和遍历集合元素。当然,作为具体集合,需要提供具体的添加、移除、查询集合长度的具体方法;

    迭代器模式如何实现

    需求描述

            回想在上大学的时候,老师在教学活动中都会拥有一个学生的花名册,上面有学生的姓名、学号等信息,每次上课前都要先按照名册上记录的顺序逐一点名,如果帮老师写一个自动点名的程序,那么这个时候使用迭代器模式绝对是非常不错的一个选择。那么具体怎么实现呢?

    实现方法

    1、定义一个迭代器接口,即名册迭代器接口,主要有两个抽象方法:判断是否下一个元素可以遍历、遍历取出下一个元素;

    1. /**
    2. * 名删除迭代器抽象接口
    3. * @param
    4. */
    5. public interface RosterInterator {
    6. /**
    7. * 是否有下一个元素
    8. * @return
    9. */
    10. boolean hasNext();
    11. /**
    12. * 取出下一个元素
    13. * @return
    14. */
    15. T next();
    16. }

    2、定义一个集合接口:抽象名册接口,定义一个抽象方法:获取名册迭代器;

    1. /**
    2. * 名册
    3. */
    4. public interface Roster {
    5. /**
    6. * 获取迭代器
    7. * @return
    8. */
    9. RosterInterator getInterator();
    10. }

    3、定义具体的集合,即具体的学生名册类,具体的学生名册类除了正常可以往学生名册上添加、移除学生,查询学生名册上人员数量方法外,还要实现抽象名册接口的获取名册迭代器的方法,在方法中创建具体的迭代器对象;

    1. /**
    2. * 学生类
    3. */
    4. @Data
    5. public class Student {
    6. private String stuNo;
    7. private String name;
    8. public Student(String stuNo, String name) {
    9. this.stuNo = stuNo;
    10. this.name = name;
    11. }
    12. }
    1. /**
    2. * 学生名册
    3. */
    4. @Data
    5. public class StudentRoster implements Roster{
    6. /**
    7. * 学生对象集合
    8. */
    9. private List list=new ArrayList<>();
    10. /**
    11. * 添加学生
    12. * @param student
    13. */
    14. public void add(Student student){
    15. this.list.add(student);
    16. }
    17. /**
    18. * 移除学生
    19. * @param student
    20. */
    21. public void remove(Student student){
    22. this.list.remove(student);
    23. }
    24. /**
    25. * 学生名册学生对象数量
    26. * @return
    27. */
    28. public Integer size(){
    29. return this.list.size();
    30. }
    31. @Override
    32. public RosterInterator getInterator() {
    33. return new StudentRosterInterator(this);
    34. }
    35. }

    4、定义集合迭代器实现,即具体的学生名册迭代器,具体的学生迭代器会持有具体的学生名册,并实现名册迭代器定义的两个抽象方法,即具体的判断是否有下一个元素可以遍历、遍历取出下一个元素;

    1. /**
    2. * 学生名册迭代器
    3. */
    4. @Data
    5. public class StudentRosterInterator implements RosterInterator{
    6. /**
    7. * 学生名册
    8. */
    9. private StudentRoster roster;
    10. /**
    11. * 索引位置
    12. */
    13. private Integer index=0;
    14. public StudentRosterInterator(StudentRoster roster) {
    15. this.roster = roster;
    16. }
    17. @Override
    18. public boolean hasNext() {
    19. return this.roster.size() > index;
    20. }
    21. @Override
    22. public Student next() {
    23. Student student = this.roster.getList().get(index);
    24. index++;
    25. return student;
    26. }
    27. }

    5、编写客户端类

    1. public class StudentClient {
    2. public static void main(String[] args) {
    3. StudentRoster studentRoster=new StudentRoster();
    4. Student stu1 = new Student("s001", "小明");
    5. Student stu2 = new Student("s002", "小红");
    6. Student stu3 = new Student("s003", "小刚");
    7. studentRoster.add(stu1);
    8. studentRoster.add(stu2);
    9. studentRoster.add(stu3);
    10. RosterInterator rosterInterator=new StudentRosterInterator(studentRoster);
    11. while (rosterInterator.hasNext()) {
    12. Student student = rosterInterator.next();
    13. System.out.println(student.getStuNo()+":"+student.getName());
    14. }
    15. }
    16. }

    如何扩展

    老师上课的时候会根据学生画名册点名,那么学校领导在给老师们开会的时候,有没有可能也会点一个名,看年哪位老师没有到?当然会。假如也要帮校长实现一个对老师们的点名程序,应该怎么在原先的基础上扩展呢?其实很简单,保持原有的抽象名册迭代器接口、抽象名册接口不变,再分别实现老师画名册、老师画名册迭代器就可。而且这一过程完全符合开闭原则,不会对原来的程序造成任何影响,这就是设计模式的魅力。

    1. /**
    2. * 老师类
    3. */
    4. @Data
    5. public class Teacher {
    6. private String teacNo;
    7. private String name;
    8. public Teacher(String teacNo, String name) {
    9. this.teacNo = teacNo;
    10. this.name = name;
    11. }
    12. }
    1. /**
    2. * 老师名册
    3. */
    4. @Data
    5. public class TeacherRoster implements Roster{
    6. /**
    7. * 老师对象集合
    8. */
    9. private List list=new ArrayList<>();
    10. /**
    11. * 添加老师
    12. * @param teacher
    13. */
    14. public void add(Teacher teacher){
    15. this.list.add(teacher);
    16. }
    17. /**
    18. * 移除老师
    19. * @param teacher
    20. */
    21. public void remove(Teacher teacher){
    22. this.list.remove(teacher);
    23. }
    24. /**
    25. * 老师名册中对象数量
    26. * @return
    27. */
    28. public Integer size(){
    29. return this.list.size();
    30. }
    31. @Override
    32. public RosterInterator getInterator() {
    33. return new TeacherRosterInterator(this) ;
    34. }
    35. }
    1. /**
    2. * 老师名册迭代器
    3. */
    4. @Data
    5. public class TeacherRosterInterator implements RosterInterator {
    6. private TeacherRoster teacherRoster;
    7. private Integer index = 0;
    8. public TeacherRosterInterator(TeacherRoster teacherRoster) {
    9. this.teacherRoster = teacherRoster;
    10. }
    11. @Override
    12. public boolean hasNext() {
    13. return this.teacherRoster.size() > index;
    14. }
    15. @Override
    16. public Teacher next() {
    17. Teacher teacher = this.teacherRoster.getList().get(index);
    18. index++;
    19. return teacher;
    20. }
    21. }
    1. public class TeacherClent {
    2. public static void main(String[] args) {
    3. TeacherRoster teacherRoster=new TeacherRoster();
    4. Teacher t1 = new Teacher("t001", "王老师");
    5. Teacher t2 = new Teacher("t002", "李老师");
    6. Teacher t3 = new Teacher("t003", "张老师");
    7. teacherRoster.add(t1);
    8. teacherRoster.add(t2);
    9. teacherRoster.add(t3);
    10. RosterInterator rosterInterator=new TeacherRosterInterator(teacherRoster);
    11. while (rosterInterator.hasNext()) {
    12. Teacher teacher = rosterInterator.next();
    13. System.out.println(teacher.getTeacNo()+":"+teacher.getName());
    14. }
    15. }
    16. }

    迭代器模式的适用场景

            业务场景具有下面的特征就可以使用迭代器模式:

    • 需要遍历集合对象中的元素,而不暴露该对象的内部表示的场景。通过使用迭代器模式,可以在不暴露集合对象内部结构的情况下,顺序访问集合对象中的各个元素。
    • 需要为遍历不同的集合结构提供统一接口的场景。迭代器模式可以提供一种统一的接口,用于遍历不同的集合结构,从而使得代码更加灵活和可扩展。

    有没有比较具体的业务场景示例呢?当然有,比如:

    1. 在物流系统中,可以使用迭代器模式来遍历物品。例如,在传送带上,不管传送的是什么物品,都被打包成一个一个的箱子并且有一个统一的二维码。这样我们不需要关心箱子里是什么物品,只需要一个一个检查发送的目的地即可。
    2. 在图片播放器中,可以使用迭代器模式来遍历图片。这样可以在不暴露图片内部结构的情况下,顺序访问图片中的各个元素。
    3. 图书馆管理系统:在图书馆中,需要对书籍进行遍历,可以使用迭代器模式来处理。通过定义一个统一的迭代器接口,不同的数据结构(如数组、链表等)都可以使用相同的迭代器接口来遍历书籍,同时保持内部实现的封装。
    4. 电商网站:在电商网站中,通常需要对商品进行展示和遍历。使用迭代器模式可以实现对商品的顺序访问,而不暴露商品内部表示。
    5. 金融系统:在金融系统中,需要对账户、交易等进行处理。使用迭代器模式可以实现对账户或交易的顺序访问,而不暴露其内部表示。
    6. 音乐播放器:在音乐播放器中,需要遍历音乐库中的歌曲。使用迭代器模式可以实现对歌曲的顺序访问,而不暴露歌曲内部表示。

    总结

    优点

    1. 分离集合与迭代逻辑:迭代器模式将集合对象与遍历逻辑分离,使得它们可以独立变化。集合对象只需要实现迭代器接口,而客户端只需要通过迭代器进行遍历操作,从而实现了解耦和模块化。
    2. 统一遍历接口:迭代器模式定义了一组统一的遍历接口,使得客户端可以以相同的方式对待不同类型的集合对象。无论是数组、链表、树状结构还是其他自定义集合,只要它们提供了符合迭代器接口的迭代器对象,就可以使用迭代器模式进行遍历,提高了代码的灵活性和可复用性。
    3. 简化客户端代码:使用迭代器模式可以简化客户端代码,减少了对集合内部结构的直接操作,只需要通过迭代器对象进行遍历操作。

    缺点

    1. 可能增加代码复杂度:使用迭代器模式可能会增加一些额外的代码复杂度,例如需要定义迭代器接口、具体迭代器实现类等。
    2. 限制集合对象的类型:迭代器模式通常只适用于集合类型的聚合对象,不能很好地处理其他类型的聚合对象,例如树形结构、图形结构等。
    3. 可能增加内存开销:使用迭代器模式可能会增加一些额外的内存开销,例如需要创建迭代器对象等。

            总之,迭代器模式在许多业务场景中都有应用,可以实现对集合对象的顺序访问,而不暴露其内部表示,可以使得代码更加清晰、简洁、易于维护。

  • 相关阅读:
    【ES-实战入门】
    「DaoCloud 道客」联合华农保险,探索保险机构上云的最佳路径
    微信小程序通过 movable-area 做一个与vuedraggable相似的上下拖动排序控件
    redis的安装与应用
    【.Net动态Web API】参数模型验证异常格式化
    项目经理,可别再用不好PEST分析法了
    安装wireshark的正确姿势
    一举两得:研究发现常吃辣椒可延长寿命和抵御寒冷
    2017英语一Text1
    网络IO概述
  • 原文地址:https://blog.csdn.net/fox9916/article/details/134216372