写点什么

行为型模式:迭代器模式解析

发布于: 2020 年 07 月 29 日
行为型模式:迭代器模式解析

ITERATOR

今天是设计模式学习系列的第10篇,今天要学习的是 -- 迭代器模式

带着问题出发

  1. 什么是迭代器模式?

  2. 迭代器解决了什么问题?

  3. 适用场景?

定义

迭代器模式 提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。

问题

一个聚合对象,如列表,数组,树型结构等,应该提供一种方法来让别人访问它的元素,而又不需要暴露内部结构。此外针对不同的需要,可能要以不同的方式遍历这个聚合对象。但是由于聚合对象内部结构不一样,遍历方式不一样,所以会对使用者造成很大的烦恼,因为要针对不同的聚合对象编写不同的代码。

迭代器模式可帮你解决这个问题,这一模式的关键思想是将对列表的访问和遍历从列表对象中分离出来并放入一个迭代器。 迭代器类定义了一个访问该聚合元素的接口。迭代器负责跟踪当前元素;即,它知道哪些元素已经遍历过了。

例如,今天你需要使用深度优先算法来遍历树结构, 明天可能会需要广度优先算法; 下周则可能会需要其他方式 (比如随机存取树中的元素)。

另一方面, 使用多种集合的客户端代码可能并不关心存储数据的方式。 不过由于集合提供不同的元素访问方式, 你的代码将不得不与特定集合类进行耦合。

解决方案

这时你可能需要一个迭代器对象,它们之间的关系如下图:

在实例化迭代器之前,必须提供待遍历的聚合对象。一旦有了该聚合对象迭代器的实例,就可以顺序的访问该列表的各个元素。

迭代器模式类图

上图也展示了,为了要使客户代码不依赖具体的聚合对象,就不能客户自己简单地实例化一个创建迭代器,而是要让聚合对象负责创建相应的迭代器。所以集合对象提供了createIterator 这种的操作。

需要说明的是这里的创建迭代器是一个 Factory Method 模式,你发现了?

适用性

迭代器模式可以用来:

  • 访问一个聚合对象的内容而无需暴露它的内部表示;

  • 支持对聚合对象的多种遍历;

  • 为遍历不同的聚合结构提供一个统一的接口(即,支持多态迭代);

带来的好处

  1. 它支持以不同的方式遍历一个集合。 复杂的聚合可用多种方式进行遍历。例如,代码生成和语义检查要遍历语法分析树。代码生成可以按照中序或者前序来遍历语法分析树。迭代器模式使得改变遍历算法变得很容易:仅需用一个不同的迭代器的实例代替之前的实例即可。你也可以自己定义迭代器的子类以支持新的遍历。

  2. 迭代器简化了聚合的接口。 有了迭代器的遍历接口,聚合本身就不再需要类似的遍历接口了。

  3. 在同一个聚合上可以有多个遍历。 每个迭代器保持它自己的遍历状态。因此你可以同时实现多个遍历。

实现

假如我们现在要做一个展示某学校的所有院系中的所有专业的需求,每个院系是独立的,意味着每个院系类中存储专业使用的数据结构可能不一样。 接下来我们就用迭代器模式来实现,一起来看下。

public class Client {
public static void main(String[] args) {
// TODO Auto-generated method stub
//创建学院
List<College> collegeList = new ArrayList<College>();
ComputerCollege computerCollege = new ComputerCollege();
InfoCollege infoCollege = new InfoCollege();
collegeList.add(computerCollege);
//collegeList.add(infoCollege);
OutPutImpl outPutImpl = new OutPutImpl(collegeList);
outPutImpl.printCollege();
}
}
public interface College {
public String getName();
//增加系的方法
public void addDepartment(String name, String desc);
//返回一个迭代器,遍历
public Iterator createIterator();
}
public class ComputerCollege implements College {
Department[] departments;
int numOfDepartment = 0 ;// 保存当前数组的对象个数
public ComputerCollege() {
departments = new Department[5];
addDepartment("Java专业", " Java专业 ");
addDepartment("PHP专业", " PHP专业 ");
addDepartment("大数据专业", " 大数据专业 ");
}
@Override
public String getName() {
// TODO Auto-generated method stub
return "计算机学院";
}
@Override
public void addDepartment(String name, String desc) {
// TODO Auto-generated method stub
Department department = new Department(name, desc);
departments[numOfDepartment] = department;
numOfDepartment += 1;
}
@Override
public Iterator createIterator() {
// TODO Auto-generated method stub
return new ComputerCollegeIterator(departments);
}
}
public class ComputerCollegeIterator implements Iterator {
//这里我们需要Department 是以怎样的方式存放=>数组
Department[] departments;
int position = 0; //遍历的位置
public ComputerCollegeIterator(Department[] departments) {
this.departments = departments;
}
//判断是否还有下一个元素
@Override
public boolean hasNext() {
// TODO Auto-generated method stub
if(position >= departments.length || departments[position] == null) {
return false;
}else {
return true;
}
}
@Override
public Object next() {
// TODO Auto-generated method stub
Department department = departments[position];
position += 1;
return department;
}
//删除的方法,默认空实现
public void remove() {
}
}
public 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;
}
}
public class InfoColleageIterator implements Iterator {
List<Department> departmentList; // 信息工程学院是以List方式存放系
int index = -1;//索引
public InfoColleageIterator(List<Department> departmentList) {
this.departmentList = departmentList;
}
//判断list中还有没有下一个元素
@Override
public boolean hasNext() {
// TODO Auto-generated method stub
if(index >= departmentList.size() - 1) {
return false;
} else {
index += 1;
return true;
}
}
@Override
public Object next() {
// TODO Auto-generated method stub
return departmentList.get(index);
}
//空实现remove
public void remove() {
}
}
public class InfoCollege implements College {
List<Department> departmentList;
public InfoCollege() {
departmentList = new ArrayList<Department>();
addDepartment("信息安全专业", " 信息安全专业 ");
addDepartment("网络安全专业", " 网络安全专业 ");
addDepartment("服务器安全专业", " 服务器安全专业 ");
}
@Override
public String getName() {
// TODO Auto-generated method stub
return "信息工程学院";
}
@Override
public void addDepartment(String name, String desc) {
// TODO Auto-generated method stub
Department department = new Department(name, desc);
departmentList.add(department);
}
@Override
public Iterator createIterator() {
// TODO Auto-generated method stub
return new InfoColleageIterator(departmentList);
}
}
public 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());
}
}
}

本文还涉及一个核心的设计原则:单一职责,一个类应该只有一个引起变化的原因。

设计模式的学习需要经过实践然后才能掌握,希望大家在理解模式的思想之后,多去思考在平时写代码中实现,很多人抱怨干的活没有工作含量,整天crud,其实不妨从用设计模式优化你的代码开始。

本文完!fighting

个人公众号



  • 觉得写得还不错的小伙伴麻烦动手点赞+关注

  • 文章如果存在不正确的地方,麻烦指出,非常感谢您的阅读;

  • 推荐大家关注我的公众号,会为你定期推送原创干货文章,拉你进优质学习社群;

  • github地址:github.com/coderluojust/qige_blogs



发布于: 2020 年 07 月 29 日阅读数: 69
用户头像

专注于Java技术栈,热爱编程的你值得拥有 2018.03.21 加入

半路出家学习编程,脚踏实地,目前就职于某世界500强。现阶段坚持写作,分享知识,形成自己的体系。 从计划到坚持,再到形成自己的节奏。fighting

评论 (2 条评论)

发布
用户头像
带着疑问往下看,具体是哪些遍历方式呢?

支持对聚合对象的多种遍历;

2020 年 07 月 29 日 19:57
回复
这块的意思是我们可以自定义遍历的顺序,改变遍历算法,只需要添加一个迭代器就好了,强调迭代器模式可以使遍历集合和集合本身解耦。
遍历的方式就有很多了,比如前序、中序、甚至可以奇数下标遍历等
2020 年 08 月 07 日 20:40
回复
没有更多了
行为型模式:迭代器模式解析