【Java】我所知道设计模式之迭代器模式

前言介绍


接下里介绍的是Java 的设计模式之一:迭代器模式

我们还是以一个问题进行展开,引入迭代器模式

编写程序展示一个学校院系结构:需求是这样,要在一个页面中展示出学校的院系组成,一个学校有多个学院, 一个学院有多个系。如图

【Java】我所知道设计模式之迭代器模式

我们之前用组合模式解决过这个问题,但是我们现在要以遍历的角度去思考

怎么遍历他们?

一、传统方式解决问题

【Java】我所知道设计模式之迭代器模式

比如说目前

计算机学院采用的是数组的方式存储、信息学院采用集合存储

那么我们怎么去遍历他们?

解决方案:=> 迭代器模式

二、什么是迭代器模式

迭代器模式(Iterator Pattern)是常用的设计模式,属于行为型模式

如果我们的集合元素是用不同的方式实现的,有数组,还有集合类,或者还有其他方式,当客户端要遍历这些集合元素的时候就要使用多种遍历方式,而且还会暴露元素的内部结构,可以考虑使用迭代器模式解决。

迭代器模式,提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层表示,即:不暴露其内部的结构

迭代器原理类图分析

【Java】我所知道设计模式之迭代器模式

Iterator :迭代器接口由系统提供,含义 hasNext, next, remove

ConcreteIterator :具体的迭代器类,管理迭代

Aggregate :一个统一的聚合接口将客户端和具体聚合解耦

ConcreteAggreage : 具体的聚合持有对象集合, 并提供一个方法,返回一个迭代器, 该迭代器可以正确遍历集合

Client :客户端, 通过 Iterator 和 Aggregate 依赖子类

三、使用迭代器模式解决问题

【Java】我所知道设计模式之迭代器模式

根据我们的思路,因为学院是包含系的,所以我们需要先创建系这个类

//系

class Department {

private String name;//名称

private String desc;//描述

public Department(String name, String desc) {

super();

this.name = name;

this.desc = desc;

}

public String getName() {return name;}

public void setName(String name) {this.name = name;}

public String getDesc() {return desc;}

public void setDesc(String desc) {this.desc = desc;}

}

假设我们计算机学院采用的是数组的方式存储相关的计算机系

那么我们根据思路创建计算机学院实现迭代器接口的实现类

class ComputerCollegeIterator implements Iterator {

//这里Department是以数组的方式存放

Department[] departments;

//遍历的位置

int position = 0;

public ComputerCollegeIterator(Department[] departments) {

this.departments = departments;

}

//判断是否还有下一个元素

@Override

public boolean hasNext() {

if (position >= departments.length || departments[position] == null) {

return false;

} else {

return true;

}

}

@Override

public Object next() {

Department department = departments[position];

position += 1;

return department;

}

//删除的方法,默认空实现

@Override

public void remove() {}

}

假设我们信息学院采用的是集合的方式存储相关的信息系

那么我们根据思路创建信息学院实现迭代器接口的实现类

class InfoColleageIterator implements Iterator {

// 信息工程学院是以 List 方式存放系

List<Department> departmentList;

//索引

int index = -1;

public InfoColleageIterator(List<Department> departmentList) {

this.departmentList = departmentList;

}

//判断 list 中还有没有下一个元素

@Override

public boolean hasNext() {

if (index >= departmentList.size() - 1) {

return false;

} else {

index += 1;

return true;

}

}

@Override

public Object next() {

return departmentList.get(index);

}

// 空 实 现 remove

@Override

public void remove() {}

}

接下来我们根据思路创建返回迭代器的接口

interface College {

//学院的名称

public String getName();

//增加系的方法

public void addDepartment(String name, String desc);

//返回一个迭代器,遍历

public Iterator createIterator();

}

我们根据思路首先创建于计算机学院对应的返回迭代器实现类

class ComputerCollege implements College {

//这里Department是以数组的方式存放

Department[] departments;

// 保存当前数组的对象个数

int numOfDepartment = 0;

@Override

public String getName() {

return "计算机学院";

}

@Override

public void addDepartment(String name, String desc) {

//根据传入的信息创建系

Department department = new Department(name, desc);

departments[numOfDepartment] = department;

numOfDepartment += 1;

}

@Override

public Iterator createIterator() {

return new ComputerCollegeIterator(departments);

}

}

我们再根据思路创建于信息学院对应的返回迭代器实现类

class InfoCollege implements College {

//集合的方式存储

List<Department> departmentList;

@Override

public String getName() {

return "信息工程学院";

}

@Override

public void addDepartment(String name, String desc) {

Department department = new Department(name, desc);

departmentList.add(department);

}

@Override

public Iterator createIterator() {

return new InfoColleageIterator(departmentList);

}

}

计算机学院使用数组的方式存储、信息学院使用集合的方式存储

这时我们创建一个输出类,通过迭代器的方式输出所有学院

class OutPutImpl {

//学院集合

List<College> collegeList;

public OutPutImpl(List<College> collegeList) {

this.collegeList = collegeList;

}

//遍历所有学院,然后调用 printDepartment 输出各个学院的系

public void printCollege() {

//从 collegeList 取出所有学院, Java 中的 List 已经实现Iterator

Iterator<College> iterator = collegeList.iterator();

while (iterator.hasNext()) {

//取出一个学院

College college = iterator.next();

System.out.println("=== " + college.getName() + "=====");

printDepartment(college.createIterator()); //得到对应迭代器

}

}

//输出 学院输出 系

public void printDepartment(Iterator iterator) {

while (iterator.hasNext()) {

Department d = (Department) iterator.next();

System.out.println(d.getName());

}

}

}

接下来我们使用demo 一起体会看看,是怎么将两组不同存储方式遍历的

public static void main(String[] args) {

//创建存储迭代类

List<College> collegeList = new ArrayList<College>();

//创建返回对应计算机学院迭代器

ComputerCollege computerCollege = new ComputerCollege();

//创建存储计算机学院的里的系

computerCollege.departments = new Department[5];

//将计算机学院里的系,存储到迭代器里

computerCollege.addDepartment("Java 专业", " Java 专业 ");

computerCollege.addDepartment("PHP 专业", " PHP 专业 ");

computerCollege.addDepartment("大数据专业", " 大数据专业 ");

//创建返回对应信息学院对应迭代器

InfoCollege infoCollege = new InfoCollege();

//创建存储计算机学院的里的系

infoCollege.departmentList = new ArrayList<Department>();

//将信息学院里的系,存储到迭代器里

infoCollege.addDepartment("信息安全专业", " 信息安全专业 ");

infoCollege.addDepartment("网络安全专业", " 网络安全专业 ");

infoCollege.addDepartment("服务器安全专业", " 服务器安全专业 ");

//将两个学院的迭代器添加进集合中

collegeList.add(computerCollege);

collegeList.add(infoCollege);

//将集合交给帮忙输出类进行输出

OutPutImpl outPutImpl = new OutPutImpl(collegeList);

outPutImpl.printCollege();

}

运行结果如下:

=== 计算机学院=====

Java 专业

PHP 专业

大数据专业

=== 信息工程学院=====

信息安全专业

网络安全专业

服务器安全专业

四、迭代器在源码中的使用

我们先使用一个示例,来看看ArrayList的使用与输出

public static void main(String[] args) {

List<String> list = new ArrayList<>();

list.add("Jack");

Iterator<String> integer = list.iterator();

while(integer.hasNext()){

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

}

}

运行结果如下:

Jack

诶,为什么ArrayList可以使用Iterator迭代器呢?

我们一起进ArrayList的源码里去看看,发现它实现了List集合的接口

public class ArrayList<E> extends AbstractList<E>

implements List<E>, RandomAccess, Cloneable, java.io.Serializable

{

//....省略关键性的代码

}

那么List集合这个接口做了什么事情?我们也进去看看

public interface List<E> extends Collection<E> {

int size();

boolean isEmpty();

boolean contains(Object o);

Iterator<E> iterator();

Object[] toArray();

//省略其他关键性代码.....

}

诶,我们发现List集合里有一个iterator方法

那么ArrayList 有没有对着这个方法实现呢?我们一起进去找找看看

public class ArrayList<E> extends AbstractList<E>

implements List<E>, RandomAccess, Cloneable, java.io.Serializable

{

public Iterator<E> iterator() {

return new Itr();

}

private class Itr implements Iterator<E> {

int cursor; // index of next element to return

int lastRet = -1; // index of last element returned; -1 if no such

int expectedModCount = modCount;

Itr() {}

public boolean hasNext() {

return cursor != size;

}

@SuppressWarnings("unchecked")

public E next() {

checkForComodification();

int i = cursor;

if (i >= size)

throw new NoSuchElementException();

Object[] elementData = ArrayList.this.elementData;

if (i >= elementData.length)

throw new ConcurrentModificationException();

cursor = i + 1;

return (E) elementData[lastRet = i];

}

public void remove() {

if (lastRet < 0)

throw new IllegalStateException();

checkForComodification();

try {

ArrayList.this.remove(lastRet);

cursor = lastRet;

lastRet = -1;

expectedModCount = modCount;

} catch (IndexOutOfBoundsException ex) {

throw new ConcurrentModificationException();

}

}

@Override

@SuppressWarnings("unchecked")

public void forEachRemaining(Consumer<? super E> consumer) {

Objects.requireNonNull(consumer);

final int size = ArrayList.this.size;

int i = cursor;

if (i >= size) {

return;

}

final Object[] elementData = ArrayList.this.elementData;

if (i >= elementData.length) {

throw new ConcurrentModificationException();

}

while (i != size && modCount == expectedModCount) {

consumer.accept((E) elementData[i++]);

}

// update once at end of iteration to reduce heap write traffic

cursor = i;

lastRet = i - 1;

checkForComodification();

}

final void checkForComodification() {

if (modCount != expectedModCount)

throw new ConcurrentModificationException();

}

}

//省略其他关键性代码....

}

我们这样就发现了,一下就思路就清醒了,请看类图分析

【Java】我所知道设计模式之迭代器模式

Itr是ArrayList的一个内部类,本身使用ArrayList里的elementData

因此与原先迭代模式不一样,它是直接使用

五、迭代器模式的注意事项和细节


Ø 优点

提供一个统一的方法遍历对象,客户不用再考虑聚合的类型,使用一种方法就可以遍历对象了。

隐藏了聚合的内部结构,客户端要遍历聚合的时候只能取到迭代器,而不会知道聚合的具体组成。

提供了一种设计思想,就是一个类应该只有一个引起变化的原因(叫做单一责任原则)。

在聚合类中,我们把迭代器分开,就是要把管理对象集合和遍历对象集合的责任分开,这样一来集合改变的话,只影响到聚合对象。

而如果遍历方式改变的话,只影响到了迭代器。

当要展示一组相似对象,或者遍历一组相同对象时使用, 适合使用迭代器模式

Ø 缺点

每个聚合对象都要一个迭代器,会生成多个迭代器不好管理类

参考资料


尚硅谷:设计模式(韩顺平老师):迭代器模式

Refactoring.Guru:《深入设计模式》

以上是 【Java】我所知道设计模式之迭代器模式 的全部内容, 来源链接: utcz.com/a/111115.html

回到顶部