文章目录
- 1 迭代器模式(Iterator Pattern)★★★
- 1.1 介绍
- 1.2 概述
- 1.3 迭代器模式的结构
- 1.4 中介者模式的优缺点
- 1.5 中介者模式的使用场景
- 2 案例一
- 2.1 需求
- 2.2 代码实现
- 3 案例二
- 3.1 需求
- 3.2 代码实现
- 4 JDK源码解析
🙊 前言:本文章为瑞_系列专栏之《23种设计模式》的迭代器模式篇。本文中的部分图和概念等资料,来源于博主学习设计模式的相关网站《菜鸟教程 | 设计模式》和《黑马程序员Java设计模式详解》,特此注明。本文中涉及到的软件设计模式的概念、背景、优点、分类、以及UML图的基本知识和设计模式的6大法则等知识,建议阅读 《瑞_23种设计模式_概述》
本系列 - 设计模式 - 链接:《瑞_23种设计模式_概述》
⬇️本系列 - 创建型模式 - 链接🔗单例模式:《瑞_23种设计模式_单例模式》
工厂模式:《瑞_23种设计模式_工厂模式》
原型模式:《瑞_23种设计模式_原型模式》
抽象工厂模式:《瑞_23种设计模式_抽象工厂模式》
建造者模式:《瑞_23种设计模式_建造者模式》⬇️本系列 - 结构型模式 - 链接🔗
代理模式:《瑞_23种设计模式_代理模式》
适配器模式:《瑞_23种设计模式_适配器模式》
装饰者模式:《瑞_23种设计模式_装饰者模式》
桥接模式:《瑞_23种设计模式_桥接模式》
外观模式:《瑞_23种设计模式_外观模式》
组合模式:《瑞_23种设计模式_组合模式》
享元模式:《瑞_23种设计模式_享元模式》⬇️本系列 - 行为型模式 - 链接🔗
模板方法模式:《瑞_23种设计模式_模板方法模式》
策略模式:《瑞_23种设计模式_策略模式》
命令模式:《瑞_23种设计模式_命令模式》
职责链模式:《瑞_23种设计模式_职责链模式》
状态模式:《瑞_23种设计模式_状态模式》
观察者模式:《瑞_23种设计模式_观察者模式》
中介者模式:《瑞_23种设计模式_中介者模式》
迭代器模式:《后续更新》
访问者模式:《后续更新》
备忘录模式:《后续更新》
解释器模式:《后续更新》
、
1 迭代器模式(Iterator Pattern)★★★
瑞:在Java中,想使用迭代器模式的话,只要让我们自己定义的容器类实现
java.util.Iterable
并实现其中的iterator()
方法使其返回一个java.util.Iterator
的实现类就可以了。
迭代器模式(Iterator Pattern)是 Java 和 .Net 编程环境中非常常用的设计模式。这种模式用于顺序访问集合对象的元素,不需要知道集合对象的底层表示。迭代器模式属于行为型模式。
瑞:行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。
瑞:行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。
迭代器模式属于:对象行为模式
1.1 介绍
-
意图:提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示。
-
主要解决:遍历一个聚合对象。
-
何时使用:多个类相互耦合,形成了网状结构。
-
如何解决:把在元素之间游走的责任交给迭代器,而不是聚合对象。
-
关键代码:定义接口:hasNext, next。
-
应用实例:
1️⃣ JAVA 中的 iterator。 -
优点:
1️⃣ 它支持以不同的方式遍历一个聚合对象。
2️⃣ 迭代器简化了聚合类。
3️⃣ 在同一个聚合上可以有多个遍历。
4️⃣ 在迭代器模式中,增加新的聚合类和迭代器类都很方便,无须修改原有代码。 -
缺点:
1️⃣ 由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。 -
使用场景:
1️⃣ 访问一个聚合对象的内容而无须暴露它的内部表示。
2️⃣ 需要为聚合对象提供多种遍历方式。
3️⃣ 为遍历不同的聚合结构提供一个统一的接口。 -
注意事项:
1️⃣ 迭代器模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明地访问集合内部的数据。
1.2 概述
定义:提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。
迭代器模式是一种强大的设计模式,它通过将遍历逻辑封装在迭代器对象中,实现了对集合对象的统一访问和操作,从而简化了客户端代码并提高了代码的可读性和可维护性。
在Java中,迭代器模式得到了广泛的应用。Java集合框架(Collections Framework)中的Iterator接口就是迭代器模式的一个典型实现。通过实现Iterator接口,Java集合类(如ArrayList、LinkedList等)提供了统一的遍历方式,使得客户端可以方便地访问和操作集合中的元素。
1.3 迭代器模式的结构
- 迭代器模式主要包含以下角色:
1️⃣ 抽象聚合(Aggregate)角色:定义存储、添加、删除聚合元素以及创建迭代器对象的接口。
2️⃣ 具体聚合(ConcreteAggregate)角色:实现抽象聚合类,返回一个具体迭代器的实例。
3️⃣ 抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、next() 等方法。
4️⃣ 具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。
1.4 中介者模式的优缺点
优点:
-
它支持以不同的方式遍历一个聚合对象,在同一个聚合对象上可以定义多种遍历方式。在迭代器模式中只需要用一个不同的迭代器来替换原有迭代器即可改变遍历算法,我们也可以自己定义迭代器的子类以支持新的遍历方式。
-
迭代器简化了聚合类。由于引入了迭代器,在原有的聚合对象中不需要再自行提供数据遍历等方法,这样可以简化聚合类的设计。
-
在迭代器模式中,由于引入了抽象层,增加新的聚合类和迭代器类都很方便,无须修改原有代码,满足 “开闭原则” 的要求。
缺点:
- 增加了类的个数,这在一定程度上增加了系统的复杂性。
1.5 中介者模式的使用场景
- 当需要为聚合对象提供多种遍历方式时。
- 当需要为遍历不同的聚合结构提供一个统一的接口时。
- 当访问一个聚合对象的内容而无须暴露其内部细节的表示时。
2 案例一
【案例】自定义迭代器
2.1 需求
定义一个可以存储学生对象的容器对象,将遍历该容器的功能交由迭代器实现。
类图如下:
2.2 代码实现
定义迭代器接口,声明hasNext、next方法
/**
* 抽象迭代器角色接口
*
* @author LiaoYuXing-Ray
**/
public interface StudentIterator {
// 判断是否还有元素
boolean hasNext();
// 获取下一个元素
Student next();
}
定义具体的迭代器类,重写所有的抽象方法
import java.util.List;
/**
* 具体迭代器角色类
*
* @author LiaoYuXing-Ray
**/
public class StudentIteratorImpl implements StudentIterator {
private final List<Student> list;
private int position = 0; // 用来记录遍历时的位置
public StudentIteratorImpl(List<Student> list) {
this.list = list;
}
public boolean hasNext() {
return position < list.size();
}
public Student next() {
// 从集合中获取指定位置的元素
Student currentStudent = list.get(position);
position++;
return currentStudent;
}
}
定义抽象容器类,包含添加元素,删除元素,获取迭代器对象的方法
/**
* 抽象聚合角色接口
*
* @author LiaoYuXing-Ray
**/
public interface StudentAggregate {
// 添加学生功能
void addStudent(Student stu);
// 删除学生功能
void removeStudent(Student stu);
// 获取迭代器对象功能
StudentIterator getStudentIterator();
}
定义具体的容器类,重写所有的方法
import java.util.ArrayList;
import java.util.List;
/**
* 具体的容器类
*
* @author LiaoYuXing-Ray
**/
public class StudentAggregateImpl implements StudentAggregate {
private final List<Student> list = new ArrayList<Student>();
public void addStudent(Student stu) {
list.add(stu);
}
public void removeStudent(Student stu) {
list.remove(stu);
}
// 获取迭代器对象
public StudentIterator getStudentIterator() {
return new StudentIteratorImpl(list);
}
}
迭代器元素类
/**
* 学生类
*
* @author LiaoYuXing-Ray
**/
public class Student {
private String name;
private String number;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", number='" + number + '\'' +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public Student(String name, String number) {
this.name = name;
this.number = number;
}
public Student() {
}
}
/**
* 测试类
*
* @author LiaoYuXing-Ray
**/
public class Client {
public static void main(String[] args) {
// 创建聚合对象
StudentAggregateImpl aggregate = new StudentAggregateImpl();
// 添加元素
aggregate.addStudent(new Student("张三","001"));
aggregate.addStudent(new Student("李四","002"));
aggregate.addStudent(new Student("王五","003"));
aggregate.addStudent(new Student("赵六","004"));
// 遍历聚合对象
// 1.获取迭代器对象
StudentIterator iterator = aggregate.getStudentIterator();
// 2.遍历
while(iterator.hasNext()) {
// 3.获取元素
Student student = iterator.next();
System.out.println(student.toString());
}
}
}
代码运行结果如下:
Student{name='张三', number='001'}
Student{name='李四', number='002'}
Student{name='王五', number='003'}
Student{name='赵六', number='004'}
3 案例二
本案例为菜鸟教程中的案例
3.1 需求
我们将创建一个叙述导航方法的 Iterator 接口和一个返回迭代器的 Container 接口。实现了 Container 接口的实体类将负责实现 Iterator 接口。
IteratorPatternDemo,我们的演示类使用实体类 NamesRepository 来打印 NamesRepository 中存储为集合的 Names。
3.2 代码实现
步骤 1
创建接口
public interface Iterator {
public boolean hasNext();
public Object next();
}
public interface Container {
public Iterator getIterator();
}
步骤 2
创建实现了 Container 接口的实体类。该类有实现了 Iterator 接口的内部类 NameIterator。
public class NameRepository implements Container {
public String[] names = {"Robert" , "John" ,"Julie" , "Lora"};
@Override
public Iterator getIterator() {
return new NameIterator();
}
private class NameIterator implements Iterator {
int index;
@Override
public boolean hasNext() {
if(index < names.length){
return true;
}
return false;
}
@Override
public Object next() {
if(this.hasNext()){
return names[index++];
}
return null;
}
}
}
步骤 3
使用 NameRepository 来获取迭代器,并打印名字。
public class IteratorPatternDemo {
public static void main(String[] args) {
NameRepository namesRepository = new NameRepository();
for(Iterator iter = namesRepository.getIterator(); iter.hasNext();){
String name = (String)iter.next();
System.out.println("Name : " + name);
}
}
}
步骤 4
执行程序,输出结果:
Name : Robert
Name : John
Name : Julie
Name : Lora
4 JDK源码解析
迭代器模式在JAVA的很多集合类中被广泛应用,接下来看看JAVA源码中是如何使用迭代器模式的。
List<String> list = new ArrayList<>();
Iterator<String> iterator = list.iterator(); //list.iterator()方法返回的肯定是Iterator接口的子实现类对象
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
看完这段代码是不是很熟悉,与我们案例一的代码实现基本类似。单列集合都使用到了迭代器,我们以ArrayList
举例来说明
- List:抽象聚合类
- ArrayList:具体的聚合类
- Iterator:抽象迭代器
- list.iterator():返回的是实现了
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; // 下一个要返回元素的索引
int lastRet = -1; // 上一个返回元素的索引
int expectedModCount = modCount;
Itr() {}
//判断是否还有元素
public boolean hasNext() {
return cursor != size;
}
//获取下一个元素
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];
}
...
}
这部分代码大致就是在 iterator
方法中返回了一个实例化的 Iterator
对象。Itr
是一个内部类,它实现了 Iterator
接口并重写了其中的抽象方法。
所以当我们在使用JAVA开发的时候,想使用迭代器模式的话,只要让我们自己定义的容器类实现
java.util.Iterable
并实现其中的iterator()方法使其返回一个java.util.Iterator
的实现类就可以了
如果觉得这篇文章对您有所帮助的话,请动动小手点波关注💗,你的点赞👍收藏⭐️转发🔗评论📝都是对博主最好的支持~