Java设计模式十八(迭代器模式)

编程

 

本篇我们采取倒叙的手法来讲解迭代器模式,先看下面一段我们在平时工作中常见的代码:

package com.mazhichu.designpatterns.iterator;

import java.util.Arrays;

import java.util.Iterator;

import java.util.List;

/**

* <p class="detail">

* 功能: 前言demo

* </p>

*

* @author Moore

* @ClassName Preface demo.

* @Version V1.0.

* @date 2019.11.22 10:27:32

*/

public class PrefaceDemo {

public static void main(String[] args) {

List<String> strList = Arrays.asList("码","之","初","在","努","力");

Iterator iterator = strList.iterator();

while (iterator.hasNext()){

System.out.println(iterator.next());

}

}

}

看一下运行结果:

 

有人要疑问了,这不就是个简单的遍历List集合吗,有什么值得拿出来说的?是的,这就是个简单的遍历,也是我们开发过程中最常见的遍历方式,但是在设计模式里,它就是陪在我们身边的最熟悉的陌生人,因为它就是迭代器模式。什么,迭代器模式就是遍历?对,迭代器模式就是遍历...

十七年前

 

如上图,让时间回到十八年前,在手机风靡之前,它可是最受年轻人欢迎、王者一样的存在,那时候只要谁带一个随身听或者是mp3在身上,那就是走在时尚前沿的人,上课时偷偷放在课桌里,耳机从衣服里穿过,从脖子后面绕到耳朵里戴着偷偷地听,回到宿舍和好兄弟或者好姐妹两个人一人一个耳机,睡在一个被窝里偷偷的听。那个年代,男生的随身听里谁没有几首四大天王、Beyond的歌,女生的mp3里谁没有几首刘若英、张韶涵等。那个青涩的年代,那段美好的时光,荏苒。

往事不可追,回忆仿佛冷风吹。让我们回到mp3身上,我们都知道打开mp3按播放键就能听歌,但是我们并不知道为什么把歌下载到内存卡里mp3就能播放,内存卡怎么存放歌曲的,我们也无需关心。

 

抽象点来说,每首歌都是类型相同的成员对象,而存储歌曲的内存卡、磁带、光碟等就是存储这些成员对象的聚合类(Aggregate Classes),其对象称为聚合对象。其主要有两个职责,一个是存储数据,另一个是遍历数据。存储数据是聚合对象的基本职责要求,也就是必备的。但是遍历数据是可以变化的,变化意味着可分离,也就是将遍历数据的行为从聚合对象中抽象分离出来,封装在其他对象里,由其他对象去管理遍历这个行为,这里的其他的对象就是“迭代器”,通过迭代器去遍历聚合对象的内部数据。

试想一下,如果内存卡没有mp3、手机等播放媒介,磁带没有随身听、复读机,光盘没有电脑、DVD等,那厂家在生产内存卡、磁带、光盘的时候,是不是既要有存储的功能,也要有能遍历它们然后播放的功能?

从设计模式的原则上来说,这种简化聚合对象的设计,符合“单一职责原则”,同时也符合“开闭原则”。
至此,我们来正式讲解迭代器模式。


什么是迭代器模式

Iterator pattern:Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.

迭代器模式:提供一种方法访问一个容器对象中各个元素,而又不暴露该对象的内部细节。它是一种行为型模式。


现在我们应该能明白,内存卡、磁带、光盘这些是聚合对象,mp3、随身听、DVD这些就是为聚合对象提供的迭代器对象,用户不用知道聚合对象的内部结构,就可以通过迭代器来遍历聚合对象(通过mp3来遍历读取内存卡里面的歌曲并播放),同时可以自己自定义遍历方式,扩展迭代器功能。

迭代器模式的四个角色

  • Iterator(抽象迭代器):它定义了访问和遍历元素的接口,声明了用于遍历数据元素的方法
  • ConcreteIterator(具体迭代器):它实现了抽象迭代器接口,完成对具体聚合对象的遍历,同时在具体迭代器中通过游标来记录在聚合对象中遍历时所处的当前位置。每一个聚合对象都应该对应一个具体的迭代器。
  • Aggregate(抽象聚合类):用于存储和管理元素对象,声明一个 createIterator() 方法用于创建一个迭代器对象,充当抽象迭代器工厂角色。
  • ConcreteAggregate(具体聚合类):实现了在抽象聚合类中声明的 createIterator() 方法,该方法返回一个与该具体聚合类对应的具体迭代器 ConcreteIterator 实例。

迭代器模式UML

 

代码实例

1、编写抽象迭代器角色

package com.mazhichu.designpatterns.iterator;

/**

* <p class="detail">

* 功能: 抽象迭代器角色

* </p>

*

* @param <E> the type parameter

* @author Moore

* @ClassName Iterator.

* @Version V1.0.

* @date 2019.11.25 17:24:43

*/

public interface Iterator<E> {

/**

* 判断是否有下一个元素

*

* @return

* @author Moore

* @date 2019.11.25 17:24:43

*/

boolean hasNext();

/**

* 取出下一个元素

*

* @return

* @author Moore

* @date 2019.11.25 17:24:43

*/

E next();

}

2、编写具体迭代器角色

package com.mazhichu.designpatterns.iterator;

/**

* <p class="detail">

* 功能: 具体迭代器角色(MP3)

* </p>

*

* @param <E> the type parameter

* @author Moore

* @ClassName Concrete iterator mp 3.

* @Version V1.0.

* @date 2019.11.25 17:25:03

*/

public class ConcreteIteratorMP3<E> implements Iterator<E> {

private E[] elements;

private int cursor = 0;

/**

* Instantiates a new Concrete iterator mp 3.

*

* @param elements the elements

*/

public ConcreteIteratorMP3(E[] elements) {

this.elements = elements;

}

/**

* 判断是否有下一个元素

*

* @return

*/

@Override

public boolean hasNext() {

return cursor != elements.length;

}

/**

* 取出下一个元素

*

* @return

*/

@Override

public E next() {

E e = null;

if(this.hasNext()){

e = elements[cursor];

cursor ++;

}

return e;

}

}

3、编写抽象聚合类角色

package com.mazhichu.designpatterns.iterator;

/**

* <p class="detail">

* 功能: 抽象聚合类角色

* </p>

*

* @param <E> the type parameter

* @author Moore

* @ClassName Aggregate.

* @Version V1.0.

* @date 2019.11.25 17:23:37

*/

public interface Aggregate<E> {

/**

* <p class="detail">

* 功能: 创建一个迭代器

* </p>

*

* @return iterator

* @author Moore

* @date 2019.11.25 17:23:37

*/

Iterator<E> createIterator();

/**

* <p class="detail">

* 功能: 添加一个元素

* </p>

*

* @param e :

* @author Moore

* @date 2019.11.25 17:23:37

*/

void add(E e);

}

4、编写具体聚合类角色

package com.mazhichu.designpatterns.iterator;

public class Test {

public static void main(String[] args) {

Aggregate memoryCard = new ConcreteAggregateMemoryCard();

memoryCard.add("17岁");

memoryCard.add("像我这样的人");

memoryCard.add("那些花儿");

memoryCard.add("平凡之路");

memoryCard.add("千千阙歌");

memoryCard.add("玻璃之情");

Iterator<String> mp3 = memoryCard.createIterator();

while(mp3.hasNext()){

String song = mp3.next();

System.out.println(song);

}

}

}

5、客户端测试

package com.mazhichu.designpatterns.iterator;

public class Test {

public static void main(String[] args) {

Aggregate memoryCard = new ConcreteAggregateMemoryCard();

memoryCard.add("17岁");

memoryCard.add("像我这样的人");

memoryCard.add("那些花儿");

memoryCard.add("平凡之路");

memoryCard.add("千千阙歌");

memoryCard.add("玻璃之情");

Iterator<String> mp3 = memoryCard.createIterator();

while(mp3.hasNext()){

String song = mp3.next();

System.out.println(song);

}

}

}


6、查看运行结果

 


通过代码,可以看出来迭代器mp3并不知道也不关心内存卡的内部结构和具体实现,只是将内存卡里面的歌曲一个个遍历出来,这就是一个简单的迭代器模式。

现在如果回头看文章开始的代码,是不是能立马意识到在运用迭代器模式呢,List就是一个抽象聚合类,ArrayList就是具体的聚合类,Iterator就是迭代器,是不是很清楚了。

 

总结

优点

  • 它支持以不同的方式遍历一个聚合对象,在同一个聚合对象上可以定义多种遍历方式。在迭代器模式中只需要用一个不同的迭代器来替换原有迭代器即可改变遍历算法,我们也可以自己定义迭代器的子类以支持新的遍历方式。
  • 使用迭代器之后,聚合类只用负责存储,满足“单一职责原则”。
  • 在迭代器模式中,由于引入了抽象层,增加新的聚合类和迭代器类都很方便,无须修改原有代码,满足“开闭原则”。

缺点

  • 存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,增加了系统的复杂性。


如果你看过JDK源码,就会发现,我们自己写的Aggregate跟Collection,List,Set、Map等差不多,或者说是它们的简化版,通常一个集合伴随这一个迭代器,既然JDK已经帮我们造好了轮子,就不建议自己再去为一个集合写一个迭代器了,站在巨人的肩膀上才能看的更远,走的更稳。本篇只是为了帮助大家理解迭代器模式。

 

文章同步公众号:码之初,每天推送Java技术文章,期待您的关注!

原创不易,转载请注明出处,谢谢!

以上是 Java设计模式十八(迭代器模式) 的全部内容, 来源链接: utcz.com/z/511277.html

回到顶部