设计模式——迭代器模式

更多内容,前往 IT-BLOG

在程序设计中,经常需要访问一个聚合对象中的各个元素,例如:我们使用 list 存储元素,通常的做法是将 list 的创建和遍历放在同一个类中。但这种方法不利于扩展,如果将存储方式更改为数组时,就需要更换迭代方式。违背了 “开闭原则”。“迭代器模式” 能较好的克服以上缺点,它在客户访问类与聚合类之间插入一个迭代器,这分离了聚合对象与遍历行为,对客户也隐藏了其内部细节,满足 “单一职责原则” 和 “开闭原则”,如 Java 中的 Collection、List、Set、Map 等都包含迭代器。

一、迭代模式基本介绍


1)、迭代器模式(Iterator Pattern):是常用的设计模式,属于行为型模式。
2)、如果集合元素是通过不同的方式实现的,有数组、list 等等,当客户端要遍历这些集合元素的时候就要使用多种遍历方式,而且还会暴露元素的内部结构,可以考虑使用迭代器模式解决。
3)、迭代器模式,提供了一种遍历集合元素的统一接口,用一致的方法遍历结合元素,不需要知道集合对象的底层表示,即:不暴露其内部的结构。
4)、缺点:每个聚合对象都要一个迭代器,增加了类的个数,在一定程度上增加了系统的复杂度且不好管理。
5)、提供了一种设计思想,就是一个类应该只有一个引起变化的原因(叫做 “单一责任原则”)。在聚合类中,我们把迭代器分开,就是要把管理对象集合和遍历对象集合的责任分开,这样一来集合改变的话,只影响到聚合对象。而如果遍历方式改变的话,只影响到迭代器。

二、迭代器模式结构类图


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

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

三、迭代器模式案例分析


【1】抽象迭代器:使用 JDK 自带的 Iterator 接口,我们将源码粘贴过来,无需自行实现。子类需要实现 hasNext 和 next 方法

 1 public interface Iterator<E> {
 2 
 3     boolean hasNext();
 4     //使用泛型 E
 5     E next();
 6 
 7     default void remove() {
 8         throw new UnsupportedOperationException("remove");
 9     }
10 
11     default void forEachRemaining(Consumer<? super E> action) {
12         Objects.requireNonNull(action);
13         while (hasNext())
14             action.accept(next());
15     }
16 }

【2】具体迭代器:定义将 List 集合包装为 Iterator 遍历的对象 ListIterator

 1 public class ListIterator implements Iterator<Object>{
 2     //定义一个 List 集合
 3     List<Phone> list;
 4     
 5     //构造器
 6     public ListIterator(List<Phone> list) {
 7         this.list = list;
 8     }
 9 
10     //获取位数
11     int index = 0;
12     @Override
13     public boolean hasNext() {
14         if(list != null && list.size()>index) {
15             return true;
16         }else {
17             return false;
18         }
19     }
20 
21     @Override
22     public Object next() {
23         Phone object = list.get(index);
24         index+=1;
25         return object;
26     }
27 
28 }

【3】具体迭代器:定义将 数组集合包装为 Iterator 遍历的对象 ArrayIterator

 1 public class ArrayIterator implements Iterator<Object>{
 2     //定义 电话数组
 3     Phone[] phones;
 4     //下标
 5     private int index = 0;
 6     //构造器
 7     public ArrayIterator(Phone[] phones) {
 8         this.phones = phones;
 9     }
10 
11     @Override
12     public boolean hasNext() {
13         if(phones[index] != null && phones.length > index) {
14             return true;
15         }else {
16             return false;
17         }
18     }
19 
20     @Override
21     public Object next() {
22         Phone phone = phones[index];
23         index+=1;
24         return phone;
25     }
26 
27 }

【4】 定义 List 与 数组中存储的对象 Phone。

 1 public class Phone {
 2     public String name;
 3     public String money;
 4     /**
 5      * @param name
 6      * @param money
 7      */
 8     public Phone(String name, String money) {
 9         super();
10         this.name = name;
11         this.money = money;
12     }
13     public String getName() {
14         return name;
15     }
16     public void setName(String name) {
17         this.name = name;
18     }
19     public String getMoney() {
20         return money;
21     }
22     public void setMoney(String money) {
23         this.money = money;
24     }
25     
26 }

【5】抽象聚合角色:手机的抽象接口 IPhone

1 public interface IPhone {
2     //获取手机名称
3     public String getName();
4     //创建一个获取 Iterator 实例的方法
5     public Iterator<Object> createIterator();
6     //增加手机的方法
7     public void add(String name,String type);
8 }

【6】具体聚合角色:小米手机集合类,通过 List 进行封装,并创建一个返回 Iterator 的方法,使用迭代的方式遍历。

 1 public class XiaoMiPhoneImpl implements IPhone{
 2     //小米使用 list 存储产品
 3     private List<Phone> xiaoMis = new ArrayList<Phone>();
 4     //构造器
 5     public XiaoMiPhoneImpl() {
 6         add("红米", "1200");
 7         add("小米6", "2300");
 8         add("小米7", "3200");
 9     }
10 
11     @Override
12     public String getName() {
13         return "====小米手机====";
14     }
15     //创建遍历器
16     @Override
17     public Iterator<Object> createIterator() {
18         return new ListIterator(xiaoMis);
19     }
20         //集合中添加小米产品
21     @Override
22     public void add(String name, String money) {
23         xiaoMis.add(new Phone(name, money));
24     }
25 
26 }

【7】具体聚合角色:华为手机集合类,通过 数组 进行封装,并创建一个返回 Iterator 的方法,使用迭代的方式遍历。

 1 public class HuaWeiPhoneImpl implements IPhone{
 2     //存储华为手机的数据
 3     Phone phone[];
 4     private int index = 0;
 5     
 6     //构造器
 7     public HuaWeiPhoneImpl() {
 8         phone = new Phone[5];
 9         add("荣耀", "1300");
10         add("华为P8", "2000");
11         add("华为P20", "8000");
12     }
13 
14     @Override
15     public String getName() {
16         return "====华为手机====";
17     }
18 
19     @Override
20     public Iterator<Object> createIterator() {
21         return new ArrayIterator(phone);
22     }
23 
24     @Override
25     public void add(String name, String money) {
26         phone[index] = new Phone(name,money);
27         index +=1;
28     }
29 
30 }

【8】构造一个数据的工厂类:将所有的品牌集合在一块,通过 Iterator 接口的方式进行遍历输出。

 1 public class OutputImpl {
 2     //定义一个集合
 3     private List<IPhone> phones;
 4     
 5     //构造器
 6     public OutputImpl(List<IPhone> phones) {
 7         this.phones = phones;
 8     }
 9 
10     //输入方法
11     public void outPrit() {
12         //先调用 list 自身带的迭代器 Iterator 
13         Iterator<IPhone> iterator = phones.iterator();
14         // 调用hasNext 方法
15         while(iterator.hasNext()) {
16             IPhone iPhones = iterator.next();
17             //手机品牌名称
18             System.out.println(iPhones.getName());
19             //遍历品牌的所有手机,使用我们自己实现的Iterator
20             pritlnPhone(iPhones.createIterator());
21         }
22     }
23 
24 
25     private void pritlnPhone(Iterator<Object> createIterator) {
26         while(createIterator.hasNext()) {
27             Phone phone = (Phone) createIterator.next();
28             System.out.println("品牌="+phone.getName()+"-----金额="+phone.getMoney());
29         }
30     }
31     
32 }

【9】客户端调用:创建手机品牌实体类,并将其组合在 List 中,调用输出工厂集合类即可。

 1 public class Client {
 2 
 3     public static void main(String[] args) {
 4         //手机店集合
 5         List<IPhone> phones = new ArrayList<IPhone>();
 6         //创建华为手机 == 数组
 7         HuaWeiPhoneImpl huaWeis = new HuaWeiPhoneImpl();
 8         //创建小米手机 == list
 9         XiaoMiPhoneImpl xiaoMis = new XiaoMiPhoneImpl();
10         
11         //将其都加入到手机店集合
12         phones.add(huaWeis);
13         phones.add(xiaoMis);
14         
15         //调用公共的输入类
16         OutputImpl outputImpl = new OutputImpl(phones);
17         outputImpl.outPrit();
18         /**
19          * 结构如下:
20          * ====华为手机====
21         品牌=荣耀-----金额=1300
22         品牌=华为P8-----金额=2000
23         品牌=华为P20-----金额=8000
24         ====小米手机====
25         品牌=红米-----金额=1200
26         品牌=小米6-----金额=2300
27         品牌=小米7-----金额=3200
28          */
29     }
30 
31 }

四、迭代器模式应用源码分析


分析一下 arrayList 的 iterator 的使用

【1】先了解下 ArrayList 的 Iterator 的使用:

 1 public class IteratorDemo {
 2 
 3     public static void main(String[] args) {
 4         List<String> a = new ArrayList<>();
 5         a.add("t");// ..
 6         // 获取到迭代器
 7         Iterator<String> Itr = a.iterator();
 8         while (Itr.hasNext()) {
 9             System.out.println(Itr.next());
10         }
11     }
12 
13 }

【2】进入 ArrayList 的源码:实现了 List 接口,实现了 Iterator 方法,返回遍历对象:Iterator。相当于具体聚合对象。

1 public class ArrayList<E> extends AbstractList<E>
2         implements List<E>
3 {       
4     ...
5         public Iterator<E> iterator() {
6         return new Itr();
7     }
8     ...
9 }

【3】进入 List 接口查看:发现包含一个 Iterator 的抽象方法 。相当于抽象聚合对象

1 public interface List<E> extends Collection<E> {
2     ...
3     Iterator<E> iterator();
4     ...
5 }

【4】我们进入返回的 Iterator 对象的类 Itr ,是 ArraList 类的内部类。查看 hasNext()方法,会发现遍历的对象是 Object[] 数组,具体的迭代器类(实现 hasNext 和 Next 方法)

 1 private class Itr implements Iterator<E> {
 2         int cursor;       // index of next element to return
 3         int lastRet = -1; // index of last element returned; -1 if no such
 4         int expectedModCount = modCount;
 5 
 6         public boolean hasNext() {
 7             return cursor != size;
 8         }
 9 
10         @SuppressWarnings("unchecked")
11         public E next() {
12             checkForComodification();
13             int i = cursor;
14             if (i >= size)
15                 throw new NoSuchElementException();
16             Object[] elementData = ArrayList.this.elementData;
17             if (i >= elementData.length)
18                 throw new ConcurrentModificationException();
19             cursor = i + 1;
20             return (E) elementData[lastRet = i];
21         }
22 
23         public void remove() {
24             if (lastRet < 0)
25                 throw new IllegalStateException();
26             checkForComodification();
27 
28             try {
29                 ArrayList.this.remove(lastRet);
30                 cursor = lastRet;
31                 lastRet = -1;
32                 expectedModCount = modCount;
33             } catch (IndexOutOfBoundsException ex) {
34                 throw new ConcurrentModificationException();
35             }
36         }
37 
38         ......
39     }

【5】上述具体迭代器角色实现了 Iterator 接口,也就是抽象迭代器角色。与我们的案例中实现同一个接口,就不展示了。

【6】源码类图展示:同时多添加了两个具体的实现类:KeyIterator 与 LinkedList

 
posted @ 2020-11-19 14:35  Java程序员进阶  阅读(192)  评论(0编辑  收藏  举报