0%

设计模式之行为型模式(四):迭代器模式

1. 迭代器模式概述

  • 概述

    • 提供一种方法依次访问一个容器对象中各个元素,而又不需暴露该对象的内部细节
  • 特点

    • 迭代器的思想是依次访问、可读不可写,适合列表对外访问、隐藏内部细节、保证安全性
    • 基本上每种语言都会在语言层面为所有列表提供迭代器,实际开发中拿来主义即可
  • Demo

    • 需求:一个类中存在一个列表,这个列表需要提供给外部类访问,但不希望外部类修改其中的数据

    • 分析:可以通过提供两个方法实现可读不可写

      • 提供一个 String next() 方法,使得外部类可以按照次序,一条一条读取数据
      • 提供一个 boolean hasNext() 方法,告知外部类是否还有下一条数据
    • 代码实现

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      public class MyList {
      private List<String> data = Arrays.asList("a", "b", "c");
      private int index = 0;

      public String next() {
      // 返回数据后,将 index 加 1,使得下次访问时返回下一条数据
      return data.get(index++);
      }

      public boolean hasNext() {
      return index < data.size();
      }
      }
    • 客户端:使用一个 while 循环访问此列表

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      public class Client {
      @Test
      public void test() {
      MyList list = new MyList();
      // 输出:abc
      while (list.hasNext()) {
      System.out.print(list.next());
      }
      }
      }
      • 因为没有给外部类暴露 data 成员变量,所以可以保证数据是安全的
      • 问题是:当遍历完成后,hasNext() 方法会一直返回 false,无法再一次遍历了,所以必须在一个合适的地方把 index 重置为 0
    • 迭代器:实际上,使用 next()hasNext() 来遍历列表是一个完全通用的方法,可以为其创建一个接口,取名 Iterator,即迭代器

      1
      2
      3
      4
      5
      6
      public interface Iterator {

      boolean hasNext();

      String next();
      }
    • 修改列表类:每次遍历时生成一个迭代器,将 index 变量放到迭代器中。因为每个迭代器都是新生成的,所以每次遍历时的 index 自然也就被重置成 0 了

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      public class MyList {
      private List<String> data = Arrays.asList("a", "b", "c");

      // 每次生成一个新的迭代器,用于遍历列表
      public Iterator iterator() {
      return new Itr();
      }

      private class Itr implements Iterator {
      private int index = 0;

      @Override
      public boolean hasNext() {
      return index < data.size();
      }

      @Override
      public String next() {
      return data.get(index++);
      }
      }
      }
    • 客户端访问此列表:迭代器的核心就在于定义出 next() 方法和 hasNext() 方法,让外部类使用这两个方法来遍历列表,以达到隐藏列表内部细节的目的

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      public class Client {
      @Test
      public void test() {
      MyList list = new MyList();
      // 获取迭代器,用于遍历列表
      Iterator iterator = list.iterator();
      // 输出:abc
      while (iterator.hasNext()) {
      System.out.print(iterator.next());
      }
      }
      }
    • Java 内置的 Iterator 接口:源码中使用了泛型使此接口更加通用

      1
      2
      3
      4
      public interface Iterator<E> {
      boolean hasNext();
      E next();
      }
    • ArrayList 源码:使用迭代器模式的部分代码

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      public class ArrayList<E> {
      ...

      public Iterator<E> iterator() {
      return new Itr();
      }

      private class Itr implements Iterator<E> {
      protected int limit = ArrayList.this.size;
      int cursor;

      public boolean hasNext() {
      return cursor < limit;
      }

      public E next() {
      ...
      }
      }
      }
    • Java 内置的 Iterable 接口:平时常用的 for-each 循环,也是迭代器模式的一种应用。在 Java 中,只要实现了 Iterable 接口的类,都被视为可迭代访问的

      1
      2
      3
      4
      public interface Iterable<T> {
      Iterator<T> iterator();
      ...
      }
    • 修改列表类继承 Iterable 接口:继承 Iterable 接口之后,就可以使用 for-each 来迭代访问列表中的数据了

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      public class MyList implements Iterable<String> {
      private List<String> data = Arrays.asList("a", "b", "c");

      @NonNull
      @Override
      public Iterator<String> iterator() {
      // 每次生成一个新的迭代器,用于遍历列表
      return new Itr();
      }

      private class Itr implements Iterator<String> {
      private int index = 0;

      @Override
      public boolean hasNext() {
      return index < data.size();
      }

      @Override
      public String next() {
      return data.get(index++);
      }
      }
      }
    • 客户端:使用 for-each 访问

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      public class Client {
      @Test
      public void test() {
      MyList list = new MyList();
      // 输出:abc
      for (String item : list) {
      System.out.print(item);
      }
      }
      }
-------------------- 本文结束感谢您的阅读 --------------------