浅谈设计模式 - 迭代器模式(十一)

前言

迭代器模式通常只需要知道该模式的实现原理和了解结构图即可,在设计模式当中自己实现的情况几乎是没有的,所以这个模式简单的过一遍。

什么是迭代器模式

定义:提供顺序的方法访问一个聚合对象的各个元素,同时又不会暴露内部的功能

迭代器模式通过接管遍历的细节,让提供方不必关心迭代的具体细节,只需要提供对应的聚合对象即可。


迭代器模式和增强的FOR循环:

Jdk1.5之后,将泛型和增强for循环加入到语言体系,可以直接对于集合的内容进行for循环查看,其实本质上还是调用了iterator方法,而java通过语法糖的形式为我们进行的遍历的隐藏。


迭代器模式结构图

迭代器模式的结构图如下:

浅谈设计模式 - 迭代器模式(十一)_迭代器模式

Aggregate:聚合对象的共同接口,定义了具备遍历功能的聚合对象,通过定义创建迭代器的接口来建立与迭代器的组合
ConcreateAggregate:具体的迭代器实现对象,通过接口方法返回具体的迭代器实现
Iterator:迭代器接口,定义迭代器的统一规范,所以派生类都需要强制按照接口标准执行迭代器的实现。保证迭代器具备相似的行为。
ConcreteIterator:具体的迭代器实现,内部聚合对象的同时,可以扩展迭代器的遍历方式,比如ListIterator。

迭代器模式特点

  • 迭代器将遍历的细节进行了封装,聚合对象不需要在关心客户端如何操作内部的变量,而是通过委托给迭代器的形式交由迭代器去完成具体的遍历细节。
  • 迭代器可以在不暴露内部结构的同时让外界可以访问到内部的聚合对象,所以即使是完全不同的对象也可以统一对待和处理。
  • 迭代器是一种职责的转移,将遍历的工作从原本的聚合对象中进行独立,能在不改动数据结构的同时改变数据的操作方式

迭代器的注意事项:

  1. 需要注意迭代器存在内部的迭代器外部的迭代器,内部的迭代器供对象本身使用不对外开放,外部的迭代器通过方法返回给调用方使用。
  2. 每个责任对应一个区域,超过区域意味多了一个责任,遍历和数据操作实际上是两个操作,应该区分对待。
  3. 注意迭代器是如何体现单一职责的原则,他剥离了遍历对象的功能,将其封装到一个迭代器内部进行使用。
  4. 好的迭代器应当具备“快速失败机制”,目的是为了防止操作内部元素的指针越界,同时及时通知客户端遍历异常。
  5. 如果想让方法不支持某些功能,最好使用异常机制提醒客户端我不支持某些操作。

案例

光有理论还是不够的,这里配合代码讲解迭代器是如何实现解耦聚合对象的遍历的。

模拟场景:

由于迭代器在实际工作中使用概率 几乎为0,这里设置的场景比较简单

我们使用window最常见的文件系统来作为案例,我们通常进入不同的磁盘,首先就是对该磁盘下面的第一级目录进行遍历,同时我们根据遍历方式的不同,可以将页面展示为分组,按照时间排序,显示不同的详略信息.....这些功能的本质都是遍历,只不过遍历的形式不同,为了实现对于菜单的不同形式遍历,我们通过定义不同迭代器来完成这一个目标。

接着,我们会发现迭代的种类丰富还不够,我们还需要迭代其他的内容,比如任务管理器需要迭代不同的进程,同样也包含了排序的或者隐藏部分进程等等一系列的功能,所以需要让不同的对象可以支撑相似的迭代操作,并且可以自由的替换迭代的方式,当然这部分功能不会放入案例部分,案例部分为简单的迭代器实现。

具体实现

在进行具体的编码之前,先检查一下需要的基本构建类:

Travelsable:定义对象具备迭代的功能接口。
MissionBoard:任务栏,贮存基本的任务信息,提供迭代器供外部展示。
TaskItemlIterator:任务迭代器,用于任务的迭代操作
ConcreteCatalogIterator:目录迭代器的实现具体子类,定义了不同的迭代种类。
TaskItem 任务项,定义一个任务的内容
Computer 电脑,只需要管理任务栏和文件管理器即可。
FileManager 文件管理器,负责管理文件夹的内容
FileIterator 文件迭代器,管理文件夹的迭代操作
FileItem 文件项

下面直接按照结构图构建具体代码:

// 电脑,只需要管理任务栏和文件管理器即可。
public class Computer {

private FileManager fileManager;
private MissionBoard missionBoard;

public Computer(FileManager fileManager, MissionBoard missionBoard) {
this.fileManager = fileManager;
this.missionBoard = missionBoard;
}

public void display(){
Iterator fileManagerIterator = fileManager.createIterator();
Iterator missionBoardIterator = missionBoard.createIterator();

while (fileManagerIterator.hasNext()){
Object next = fileManagerIterator.next();
System.out.println(next);
}

while (missionBoardIterator.hasNext()){
Object next = missionBoardIterator.next();
System.out.println(next);
}

}
}

// 文件项
public class FileItem {

private String fileName;

private String editDate;

private String ceateDate;

private long size;

// 省略部分内容
}

// 任务项
public class TaskItem {

private String name;

private int size;

// 省略部分内容
}

// 迭代器规范接口
public interface Travelsable<E extends Object>{

/**
* 创建迭代器的方法
* @return
*/
Iterator<E> createIterator();
}

// 任务栏,管理任务项. 实现接口,支持迭代操作
public class MissionBoard implements Travelsable {

private Stack<TaskItem> taskItems;

public MissionBoard() {
taskItems = new Stack<>();
taskItems.push(new TaskItem("任务1", 10));
taskItems.push(new TaskItem("任务2", 1230));
taskItems.push(new TaskItem("任务3", 123));
taskItems.push(new TaskItem("任务4", 414));
taskItems.push(new TaskItem("任务5", 555));
}

@Override
public Iterator createIterator() {
return new TaskItemlIterator(taskItems);
}

}

// 文件管理器,管理文件项,实现接口并且支持迭代操作
public class FileManager implements Travelsable{

private FileItem[] fileItems;

public FileManager() {
this.fileItems = new FileItem[10];
Random random = new Random(10000);
for (int i = 0; i < fileItems.length; i++) {
fileItems[i] = new FileItem("文件"+i, random.nextInt(2000));
}
}

@Override
public Iterator createIterator() {
return new FileIterator(fileItems);
}

}

//文件迭代器
public class FileIterator implements Iterator{

private FileItem[] fileItems;

private int index;

public FileIterator(FileItem[] fileItems) {
this.fileItems = fileItems;
this.index = 0;
}

@Override
public boolean hasNext() {
return index++ < fileItems.length - 1;
}

@Override
public Object next() {
return fileItems[index];
}

@Override
public void remove(Object ele) {
throw new UnsupportedOperationException("数组不支持当前操作");
}
}

// 任务迭代器
public class TaskItemlIterator implements Iterator{

private Stack<TaskItem> taskItems;

public TaskItemlIterator(Stack<TaskItem> taskItems) {
this.taskItems = taskItems;
}

@Override
public boolean hasNext() {
if(taskItems.isEmpty()){
return false;
}
TaskItem peek = taskItems.peek();
return peek != null;
}

@Override
public Object next() {
return taskItems.pop();
}

@Override
public void remove(Object ele) {
taskItems.remove(ele);
}
}

以上就是迭代的大致实现案例代码,现代编程基本不会自己去设计迭代器,所以了解概念和知道样板代码即可。

总结:

迭代器在JAVA语言中基本已经实现到集合当中,当我们遍历集合的时候,其实就是在使用迭代器,迭代器通过封装遍历解耦了一堆对象的遍历和创建工作,将迭代的细节封装到一个黑盒当中,外部只需要调用接口就可以操作集合的数据。