设计模式之迭代器模式
迭代器模式又称游标模式,属于行为型模式;指提供一些方法来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表象。迭代器模式是通过将聚合对象的遍历行为分离出来,抽象成迭代器类来实现的,其目的是在不暴露聚合对象的内部结构的情况下,让外部代码透明地访问聚合的内部数据。
迭代器模式是最常见的几个设计模式之一,也是被广泛地应用到Java语言API中的几个设计模式之一。在Java语言的集合(Collection)框架中,广泛使用迭代器来遍历聚集的元素。
迭代器模式的UML类图如下:
从上图可知,迭代器模式涉及到抽象聚合角色、具体聚合角色、抽象迭代器角色、具体迭代器角色等四种角色:
- 抽象聚合(Aggregate)角色:定义存储、添加、删除聚合对象以及创建迭代器对象的接口。
- 具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。
- 抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、first()、next() 等方法。
- 具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。
迭代器分为主动迭代和被动迭代,主要是相对于客户端而言的。如果客户端控制迭代的进程,那么这样的迭代就是主动迭代,相反就是被动迭代。使用主动迭代的客户端会明显调用迭代器的next()等迭代方法,在遍历的过程中向前迭代,而客户端在使用被动迭代时,明显不会调用迭代方法,而是由迭代器自行推进遍历过程。而我们在开发过程中大部分都是使用的主动迭代。
学校院系例子
在一个大学里,由很多个学院组成,每个学院下又有很多系。如果要知道这个大学由多少个学院,一个学院由多少个专业,就可以使用迭代器模式。
该系统的UML类图如下:
抽象聚合角色:
package com.charon.iterator;
import java.util.Iterator;
/**
* @className: College
* @description: 学院
* @author: charon
* @create: 2022-03-27 14:52
*/
public interface College {
/**
* 获取学院的名称
* @return
*/
String getName();
/**
* 添加学院
* @param name 名称
* @param desc 描述
*/
void addDepartment(String name,String desc);
/**
* 返回一个迭代器对象,用于遍历
* @return
*/
Iterator createIterator();
}
具体聚合角色:
package com.charon.iterator;
import java.util.Iterator;
/**
* @className: ComputerCollege
* @description: 计算机学院
* @author: charon
* @create: 2022-03-27 14:56
*/
public class ComputerCollege implements College{
Department[] departments;
/**
* 下面系的个数
*/
int departmentNum;
public ComputerCollege() {
departments = new Department[3];
addDepartment("计算机科学与技术","计算机科学与技术");
addDepartment("电子科学与技术","电子科学与技术");
addDepartment("信息与通信工程","信息与通信工程");
}
@Override
public String getName() {
return "计算机学院";
}
@Override
public void addDepartment(String name, String desc) {
Department department = new Department(name, desc);
departments[departmentNum] = department;
departmentNum++;
}
@Override
public Iterator createIterator() {
return new ComputerCollegeIterator(departments);
}
}
package com.charon.iterator;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* @className: InfoCollege
* @description: 信息工程学院
* @author: charon
* @create: 2022-03-27 15:11
*/
public class InfoCollege implements College{
List<Department> departments;
public InfoCollege() {
departments = new ArrayList<>(5);
addDepartment("电气工程及自动化","电气工程及自动化");
addDepartment("电子信息工程","电子信息工程");
addDepartment("测控技术与仪器","测控技术与仪器");
addDepartment("电子科学与技术","电子科学与技术");
addDepartment("物联网工程","物联网工程");
addDepartment("通信工程","通信工程");
}
@Override
public String getName() {
return "信息工程学院";
}
@Override
public void addDepartment(String name, String desc) {
Department department = new Department(name, desc);
departments.add(department);
}
@Override
public Iterator createIterator() {
return new InfoCollegeIterator(departments);
}
}
具体迭代器角色:
package com.charon.iterator;
import java.util.Iterator;
/**
* @className: ComputerCollegeIterator
* @description:
* @author: charon
* @create: 2022-03-27 15:05
*/
public class ComputerCollegeIterator implements Iterator {
/**
* 存放专业的容器
*/
Department[] departments;
/**
* 游标,用于记录遍历的位置
*/
int cursor;
public ComputerCollegeIterator(Department[] departments) {
this.departments = departments;
}
@Override
public boolean hasNext() {
return cursor < departments.length && departments[cursor] != null;
}
@Override
public Object next() {
Department department = departments[cursor];
cursor++;
return department;
}
}
package com.charon.iterator;
import java.util.Iterator;
import java.util.List;
/**
* @className: InfoCollegeIterator
* @description:
* @author: charon
* @create: 2022-03-27 15:16
*/
public class InfoCollegeIterator implements Iterator {
List<Department> departments;
/**
* 数组下标
*/
int index;
public InfoCollegeIterator(List<Department> departments) {
this.departments = departments;
}
@Override
public boolean hasNext() {
if (index >= departments.size() -1){
return false;
}
index++;
return true;
}
@Override
public Object next() {
return departments.get(index);
}
}
其他:
package com.charon.iterator;
/**
* @className: Department
* @description: 系
* @author: charon
* @create: 2022-03-27 14:58
*/
public class Department {
private String name;
private String desc;
public Department(String name, String desc) {
this.name = name;
this.desc = desc;
}
/**
* Gets the value of name
*
* @return the value of name
*/
public String getName() {
return name;
}
}
package com.charon.iterator;
import java.util.Iterator;
import java.util.List;
/**
* @className: OutputImpl
* @description:
* @author: charon
* @create: 2022-03-27 15:19
*/
public class OutputImpl {
List<College> colleges;
public OutputImpl(List<College> colleges) {
this.colleges = colleges;
}
/**
* 遍历所有的学院,并依次输出
*/
public void printCollege(){
Iterator<College> iterator = colleges.iterator();
while (iterator.hasNext()){
// 取出一个学院
College college = iterator.next();
System.out.println("学院:" + college.getName());
printDepartment(college.createIterator());
}
}
/**
* 输出专业
* @param iterator
*/
private void printDepartment(Iterator iterator) {
while (iterator.hasNext()){
Department department = (Department) iterator.next();
System.out.println("该学院下的专业有:" + department.getName());
}
}
}
测试:
package com.charon.iterator;
import java.util.ArrayList;
import java.util.List;
/**
* @className: Client
* @description:
* @author: charon
* @create: 2022-03-27 15:27
*/
public class Client {
public static void main(String[] args) {
// 创建学院
List<College> colleges = new ArrayList<>(2);
colleges.add(new ComputerCollege());
colleges.add(new InfoCollege());
OutputImpl output = new OutputImpl(colleges);
output.printCollege();
}
}
打印:
学院:计算机学院
该学院下的专业有:计算机科学与技术
该学院下的专业有:电子科学与技术
该学院下的专业有:信息与通信工程
学院:信息工程学院
该学院下的专业有:电子信息工程
该学院下的专业有:测控技术与仪器
该学院下的专业有:电子科学与技术
该学院下的专业有:物联网工程
该学院下的专业有:通信工程
迭代器模式的优点如下:
- 访问一个聚合对象的内容而无须暴露它的内部表示。
- 遍历任务交由迭代器完成,这简化了聚合类。
- 它支持以不同方式遍历一个聚合,甚至可以自定义迭代器的子类以支持新的遍历。
- 增加新的聚合类和迭代器类都很方便,无须修改原有代码。
- 封装性良好,为遍历不同的聚合结构提供一个统一的接口。
迭代器模式的缺点如下:
- 增加了类的个数,这在一定程度上增加了系统的复杂性
迭代器模式的应用场景
由于聚合与迭代器的关系非常密切,所以大多数语言在实现聚合类时都提供了迭代器类,因此大数情况下使用语言中已有的聚合类的迭代器就已经够了。
迭代器模式通常在以下几种情况使用。
- 当需要为聚合对象提供多种遍历方式时。
- 当需要为遍历不同的聚合结构提供一个统一的接口时。
- 当访问一个聚合对象的内容而无须暴露其内部细节的表示时。
本文版权归Charon和博客园共有,原创文章,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。