• 34、迭代器模式(详解版)


    在现实生活以及程序设计中,经常要访问一个聚合对象中的各个元素,如“数据结构”中的链表遍历,通常的做法是将链表的创建和遍历都放在同一个类中,但这种方式不利于程序的扩展,如果要更换遍历方法就必须修改程序源代码,这违背了 “开闭原则”。

    既然将遍历方法封装在聚合类中不可取,那么聚合类中不提供遍历方法,将遍历方法由用户自己实现是否可行呢?答案是同样不可取,因为这种方式会存在两个缺点:

    1. 暴露了聚合类的内部表示,使其数据不安全;
    2. 增加了客户的负担。


    “迭代器模式”能较好地克服以上缺点,它在客户访问类与聚合类之间插入一个迭代器,这分离了聚合对象与其遍历行为,对客户也隐藏了其内部细节,且满足“单一职责原则”和“开闭原则”,如 Java 中的 Collection、List、Set、Map 等都包含了迭代器。

    迭代器模式在生活中应用的比较广泛,比如:物流系统中的传送带,不管传送的是什么物品,都会被打包成一个个箱子,并且有一个统一的二维码。这样我们不需要关心箱子里是什么,在分发时只需要一个个检查发送的目的地即可。再比如,我们平时乘坐交通工具,都是统一刷卡或者刷脸进站,而不需要关心是男性还是女性、是残疾人还是正常人等信息。

    模式的定义与特点

    迭代器(Iterator)模式的定义:提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。迭代器模式是一种对象行为型模式,其主要优点如下。

    1. 访问一个聚合对象的内容而无须暴露它的内部表示。
    2. 遍历任务交由迭代器完成,这简化了聚合类。
    3. 它支持以不同方式遍历一个聚合,甚至可以自定义迭代器的子类以支持新的遍历。
    4. 增加新的聚合类和迭代器类都很方便,无须修改原有代码。
    5. 封装性良好,为遍历不同的聚合结构提供一个统一的接口。


    其主要缺点是:增加了类的个数,这在一定程度上增加了系统的复杂性。

    在日常开发中,我们几乎不会自己写迭代器。除非需要定制一个自己实现的数据结构对应的迭代器,否则,开源框架提供的 API 完全够用。

    模式的结构与实现

    迭代器模式是通过将聚合对象的遍历行为分离出来,抽象成迭代器类来实现的,其目的是在不暴露聚合对象的内部结构的情况下,让外部代码透明地访问聚合的内部数据。现在我们来分析其基本结构与实现方法。

    1. 模式的结构

    迭代器模式主要包含以下角色。

    1. 抽象聚合(Aggregate)角色:定义存储、添加、删除聚合对象以及创建迭代器对象的接口。
    2. 具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。
    3. 抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、first()、next() 等方法。
    4. 具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。


    其结构图如图 1 所示。

    迭代器模式的结构图
    图1 迭代器模式的结构图

    2. 模式的实现

    迭代器模式的实现代码如下:

    1. package net.biancheng.c.iterator;
    2. import java.util.*;
    3. public class IteratorPattern {
    4. public static void main(String[] args) {
    5. Aggregate ag = new ConcreteAggregate();
    6. ag.add("中山大学");
    7. ag.add("华南理工");
    8. ag.add("韶关学院");
    9. System.out.print("聚合的内容有:");
    10. Iterator it = ag.getIterator();
    11. while (it.hasNext()) {
    12. Object ob = it.next();
    13. System.out.print(ob.toString() + "\t");
    14. }
    15. Object ob = it.first();
    16. System.out.println("\nFirst:" + ob.toString());
    17. }
    18. }
    19. //抽象聚合
    20. interface Aggregate {
    21. public void add(Object obj);
    22. public void remove(Object obj);
    23. public Iterator getIterator();
    24. }
    25. //具体聚合
    26. class ConcreteAggregate implements Aggregate {
    27. private List<Object> list = new ArrayList<Object>();
    28. public void add(Object obj) {
    29. list.add(obj);
    30. }
    31. public void remove(Object obj) {
    32. list.remove(obj);
    33. }
    34. public Iterator getIterator() {
    35. return (new ConcreteIterator(list));
    36. }
    37. }
    38. //抽象迭代器
    39. interface Iterator {
    40. Object first();
    41. Object next();
    42. boolean hasNext();
    43. }
    44. //具体迭代器
    45. class ConcreteIterator implements Iterator {
    46. private List<Object> list = null;
    47. private int index = -1;
    48. public ConcreteIterator(List<Object> list) {
    49. this.list = list;
    50. }
    51. public boolean hasNext() {
    52. if (index < list.size() - 1) {
    53. return true;
    54. } else {
    55. return false;
    56. }
    57. }
    58. public Object first() {
    59. index = 0;
    60. Object obj = list.get(index);
    61. ;
    62. return obj;
    63. }
    64. public Object next() {
    65. Object obj = null;
    66. if (this.hasNext()) {
    67. obj = list.get(++index);
    68. }
    69. return obj;
    70. }
    71. }

    程序运行结果如下:

    聚合的内容有:中山大学    华南理工    韶关学院   
    First:中山大学

    模式的应用实例

    【例1】用迭代器模式编写一个浏览婺源旅游风景图的程序。

    分析:婺源的名胜古迹较多,要设计一个查看相关景点图片(点此下载本实例所要显示的景点图片)和简介的程序,用“迭代器模式”设计比较合适。

    首先,设计一个婺源景点(WyViewSpot)类来保存每张图片的名称与简介;再设计一个景点集(ViewSpotSet)接口,它是抽象聚合类,提供了增加和删除婺源景点的方法,以及获取迭代器的方法。

    然后,定义一个婺源景点集(WyViewSpotSet)类,它是具体聚合类,用 ArrayList 来保存所有景点信息,并实现父类中的抽象方法;再定义婺源景点的抽象迭代器(ViewSpotltemtor)接口,其中包含了查看景点信息的相关方法。

    最后,定义婺源景点的具体迭代器(WyViewSpotlterator)类,它实现了父类的抽象方法;客户端程序设计成窗口程序,它初始化婺源景点集(ViewSpotSet)中的数据,并实现 ActionListener 接口,它通过婺源景点迭代器(ViewSpotlterator)来査看婺源景点(WyViewSpot)的信息。图 2 所示是其结构图。

    婺源旅游风景图浏览程序的结构图
    图2 婺源旅游风景图浏览程序的结构图(点此查看原图


    程序代码如下:

    1. package net.biancheng.c.iterator;
    2. import javax.swing.*;
    3. import java.awt.*;
    4. import java.awt.event.ActionEvent;
    5. import java.awt.event.ActionListener;
    6. import java.util.ArrayList;
    7. public class PictureIterator {
    8. public static void main(String[] args) {
    9. new PictureFrame();
    10. }
    11. }
    12. //相框类
    13. class PictureFrame extends JFrame implements ActionListener {
    14. private static final long serialVersionUID = 1L;
    15. ViewSpotSet ag; //婺源景点集接口
    16. ViewSpotIterator it; //婺源景点迭代器接口
    17. WyViewSpot ob; //婺源景点类
    18. PictureFrame() {
    19. super("中国最美乡村“婺源”的部分风景图");
    20. this.setResizable(false);
    21. ag = new WyViewSpotSet();
    22. ag.add(new WyViewSpot("江湾", "江湾景区是婺源的一个国家5A级旅游景区,景区内有萧江宗祠、永思街、滕家老屋、婺源人家、乡贤园、百工坊等一大批古建筑,精美绝伦,做工精细。"));
    23. ag.add(new WyViewSpot("李坑", "李坑村是一个以李姓聚居为主的古村落,是国家4A级旅游景区,其建筑风格独特,是著名的徽派建筑,给人一种安静、祥和的感觉。"));
    24. ag.add(new WyViewSpot("思溪延村", "思溪延村位于婺源县思口镇境内,始建于南宋庆元五年(1199年),当时建村者俞氏以(鱼)思清溪水而名。"));
    25. ag.add(new WyViewSpot("晓起村", "晓起有“中国茶文化第一村”与“国家级生态示范村”之美誉,村屋多为清代建筑,风格各具特色,村中小巷均铺青石,曲曲折折,回环如棋局。"));
    26. ag.add(new WyViewSpot("菊径村", "菊径村形状为山环水绕型,小河成大半圆型,绕村庄将近一周,四周为高山环绕,符合中国的八卦“后山前水”设计,当地人称“脸盆村”。"));
    27. ag.add(new WyViewSpot("篁岭", "篁岭是著名的“晒秋”文化起源地,也是一座距今近六百历史的徽州古村;篁岭属典型山居村落,民居围绕水口呈扇形梯状错落排布。"));
    28. ag.add(new WyViewSpot("彩虹桥", "彩虹桥是婺源颇有特色的带顶的桥——廊桥,其不仅造型优美,而且它可在雨天里供行人歇脚,其名取自唐诗“两水夹明镜,双桥落彩虹”。"));
    29. ag.add(new WyViewSpot("卧龙谷", "卧龙谷是国家4A级旅游区,这里飞泉瀑流泄银吐玉、彩池幽潭碧绿清新、山峰岩石挺拔奇巧,活脱脱一幅天然泼墨山水画。"));
    30. it = ag.getIterator(); //获取婺源景点迭代器
    31. ob = it.first();
    32. this.showPicture(ob.getName(), ob.getIntroduce());
    33. }
    34. //显示图片
    35. void showPicture(String Name, String Introduce) {
    36. Container cp = this.getContentPane();
    37. JPanel picturePanel = new JPanel();
    38. JPanel controlPanel = new JPanel();
    39. String FileName = "src/iterator/Picture/" + Name + ".jpg";
    40. JLabel lb = new JLabel(Name, new ImageIcon(FileName), JLabel.CENTER);
    41. JTextArea ta = new JTextArea(Introduce);
    42. lb.setHorizontalTextPosition(JLabel.CENTER);
    43. lb.setVerticalTextPosition(JLabel.TOP);
    44. lb.setFont(new Font("宋体", Font.BOLD, 20));
    45. ta.setLineWrap(true);
    46. ta.setEditable(false);
    47. //ta.setBackground(Color.orange);
    48. picturePanel.setLayout(new BorderLayout(5, 5));
    49. picturePanel.add("Center", lb);
    50. picturePanel.add("South", ta);
    51. JButton first, last, next, previous;
    52. first = new JButton("第一张");
    53. next = new JButton("下一张");
    54. previous = new JButton("上一张");
    55. last = new JButton("最末张");
    56. first.addActionListener(this);
    57. next.addActionListener(this);
    58. previous.addActionListener(this);
    59. last.addActionListener(this);
    60. controlPanel.add(first);
    61. controlPanel.add(next);
    62. controlPanel.add(previous);
    63. controlPanel.add(last);
    64. cp.add("Center", picturePanel);
    65. cp.add("South", controlPanel);
    66. this.setSize(630, 550);
    67. this.setVisible(true);
    68. this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    69. }
    70. @Override
    71. public void actionPerformed(ActionEvent arg0) {
    72. String command = arg0.getActionCommand();
    73. if (command.equals("第一张")) {
    74. ob = it.first();
    75. this.showPicture(ob.getName(), ob.getIntroduce());
    76. } else if (command.equals("下一张")) {
    77. ob = it.next();
    78. this.showPicture(ob.getName(), ob.getIntroduce());
    79. } else if (command.equals("上一张")) {
    80. ob = it.previous();
    81. this.showPicture(ob.getName(), ob.getIntroduce());
    82. } else if (command.equals("最末张")) {
    83. ob = it.last();
    84. this.showPicture(ob.getName(), ob.getIntroduce());
    85. }
    86. }
    87. }
    88. //婺源景点类
    89. class WyViewSpot {
    90. private String Name;
    91. private String Introduce;
    92. WyViewSpot(String Name, String Introduce) {
    93. this.Name = Name;
    94. this.Introduce = Introduce;
    95. }
    96. public String getName() {
    97. return Name;
    98. }
    99. public String getIntroduce() {
    100. return Introduce;
    101. }
    102. }
    103. //抽象聚合:婺源景点集接口
    104. interface ViewSpotSet {
    105. void add(WyViewSpot obj);
    106. void remove(WyViewSpot obj);
    107. ViewSpotIterator getIterator();
    108. }
    109. //具体聚合:婺源景点集
    110. class WyViewSpotSet implements ViewSpotSet {
    111. private ArrayList<WyViewSpot> list = new ArrayList<WyViewSpot>();
    112. public void add(WyViewSpot obj) {
    113. list.add(obj);
    114. }
    115. public void remove(WyViewSpot obj) {
    116. list.remove(obj);
    117. }
    118. public ViewSpotIterator getIterator() {
    119. return (new WyViewSpotIterator(list));
    120. }
    121. }
    122. //抽象迭代器:婺源景点迭代器接口
    123. interface ViewSpotIterator {
    124. boolean hasNext();
    125. WyViewSpot first();
    126. WyViewSpot next();
    127. WyViewSpot previous();
    128. WyViewSpot last();
    129. }
    130. //具体迭代器:婺源景点迭代器
    131. class WyViewSpotIterator implements ViewSpotIterator {
    132. private ArrayList<WyViewSpot> list = null;
    133. private int index = -1;
    134. WyViewSpot obj = null;
    135. public WyViewSpotIterator(ArrayList<WyViewSpot> list) {
    136. this.list = list;
    137. }
    138. public boolean hasNext() {
    139. if (index < list.size() - 1) {
    140. return true;
    141. } else {
    142. return false;
    143. }
    144. }
    145. public WyViewSpot first() {
    146. index = 0;
    147. obj = list.get(index);
    148. return obj;
    149. }
    150. public WyViewSpot next() {
    151. if (this.hasNext()) {
    152. obj = list.get(++index);
    153. }
    154. return obj;
    155. }
    156. public WyViewSpot previous() {
    157. if (index > 0) {
    158. obj = list.get(--index);
    159. }
    160. return obj;
    161. }
    162. public WyViewSpot last() {
    163. index = list.size() - 1;
    164. obj = list.get(index);
    165. return obj;
    166. }
    167. }

    程序运行结果如图 3 所示。

    婺源旅游风景图浏览程序的运行结果
    图3 婺源旅游风景图浏览程序的运行结果(点此查看原图

    模式的应用场景

    前面介绍了关于迭代器模式的结构与特点,下面介绍其应用场景,迭代器模式通常在以下几种情况使用。

    1. 当需要为聚合对象提供多种遍历方式时。
    2. 当需要为遍历不同的聚合结构提供一个统一的接口时。
    3. 当访问一个聚合对象的内容而无须暴露其内部细节的表示时。


    由于聚合与迭代器的关系非常密切,所以大多数语言在实现聚合类时都提供了迭代器类,因此大数情况下使用语言中已有的聚合类的迭代器就已经够了。

    模式的扩展

    迭代器模式常常与组合模式结合起来使用,在对组合模式中的容器构件进行访问时,经常将迭代器潜藏在组合模式的容器构成类中。当然,也可以构造一个外部迭代器来对容器构件进行访问,其结构图如图 4 所示。

    组合迭代器模式的结构图
    图4 组合迭代器模式的结构图
  • 相关阅读:
    redis发布订阅
    redis学习笔记(面试题)
    redis安全 (error) NOAUTH Authentication required
    HDU3001 Travelling —— 状压DP(三进制)
    POJ3616 Milking Time —— DP
    POJ3186 Treats for the Cows —— DP
    HDU1074 Doing Homework —— 状压DP
    POJ1661 Help Jimmy —— DP
    HDU1260 Tickets —— DP
    HDU1176 免费馅饼 —— DP
  • 原文地址:https://www.cnblogs.com/phpcqy/p/14279894.html
Copyright © 2020-2023  润新知