前言:
为什么之前写过Golang 版的设计模式,还在重新写Java
版?
答:因为对于我而言,当然也希望对正在学习的大伙有帮助。Java作为一门纯面向对象的语言,更适合用于学习设计模式。
为什么类图要附上uml
因为很多人学习有做笔记的习惯,如果单纯的只是放一张图片,那么学习者也只能复制一张图片,可复用性较低,附上uml,方便有新理解时,快速出新图。
🔥[设计模式Java实现附plantuml源码]专链
- 创建型
- 确保对象的唯一性~单例模式
- 集中式工厂的实现~简单工厂模式
- 多态工厂的实现——工厂方法模式
- 产品族的创建——抽象工厂模式
- 对象的克隆~原型模式
- 复杂对象的组装与创建——建造者模式
- 结构型
- 提供统一入口——外观模式
- 扩展系统功能——装饰模式
- 树形结构的处理——组合模式
- 对象的间接访问——代理模式
- 不兼容结构的协调——适配器模式
- 处理多维度变化——桥接模式
- 实现对象的复用——享元模式
- 行为型
-
请求的链式处理——职责链模式
-
请求发送者与接收者解耦——命令模式
-
遍历聚合对象中的元素——迭代器模式
文章目录
- 简单代码实现
- 使用内部类实现迭代器
- 简单代码使用内部类实现
- `JDK`内置的迭代器
- 总结
- 主要优点
- 主要缺点
- 适用场景
在软件开发时,经常需要使用聚合对象来存储一系列数据。聚合对象拥有两个职责:一是存储数据;二是遍历数据。从依赖性来看,前者是聚合对象的基本职责;而后者既是可变化的,又是可分离的。因此,可以将遍历数据的行为从聚合对象中分离出来,封装在一个被称之为“迭代器”的对象中。由迭代器来提供遍历聚合对象内部数据的行为,这将简化聚合对象的设计,更符合单一职责原则的要求。
迭代器模式定义如下:
迭代器模式(Iterator Pattern):提供一种方法来访问聚合对象,而不用暴露这个对象的内部表示,其别名为游标(Cursor)。迭代器模式是一种对象行为型模式。
在迭代器模式结构中包含聚合和迭代器两个层次结构。考虑到系统的灵活性和可扩展性,在迭代器模式中应用了工厂方法模式,其模式结构如图所示。
@startuml
interface Iterator {
+ first()
+ next()
+ hasNext()
+ currentItem()
}
class ConcreteIterator implements Iterator {
+ first()
+ next()
+ hasNext()
+ currentItem()
}
abstract class Aggregate {
+ createIterator()
}
class ConcreteAggregate extends Aggregate {
+ createIterator()
}
ConcreteAggregate .right.> ConcreteIterator: 创建迭代器
ConcreteIterator -left-> ConcreteAggregate: aggregate
@enduml
由图可以看出,在迭代器模式结构图中包含以下4个角色。
(1)Iterator(抽象迭代器):它定义了访问和遍历元素的接口,声明了用于遍历数据元素的方法。例如,用于获取第一个元素的 first()
方法,用于访问下一个元素的 next()
方法,用于判断是否还有下一个元素的 hasNext()
方法,用于获取当前元素的 currentItem()
方法等。在具体迭代器中将实现这些方法。
(2)ConcreteIterator(具体迭代器):它实现了抽象迭代器接口,完成对聚合对象的遍历,同时在具体迭代器中通过游标来记录在聚合对象中所处的当前位置。在具体实现时,游标通常是一个表示位置的非负整数。
3)Aggregate(抽象聚合类):它用于存储和管理元素对象,声明一个 createIterator()
方法用于创建一个迭代器对象,充当抽象迭代器工厂角色。
(4)ConcreteAggregate(具体聚合类):它实现了在抽象聚合类中声明的 createIterator()
方法,该方法返回一个与该具体聚合类对应的具体迭代器ConcreteIterator实例。
在迭代器模式中,提供了一个外部的迭代器来对聚合对象进行访问和遍历。迭代器定义了一个访问该聚合元素的接口,并且可以跟踪当前遍历的元素,了解哪些元素已经遍历过而哪些没有。迭代器的引入,将使得对一个复杂聚合对象的操作变得简单。
简单代码实现
package behavior;
import java.util.ArrayList;
import java.util.List;
public class IteratorDemo {
public static void main(String[] args) {
ConcreteAggregate aggregate = new ConcreteAggregate(List.of("a", "b", "c"));
Iterator<String> iterator = aggregate.createIterator();
System.out.println(iterator.first());
System.out.println(iterator.next());
System.out.println(iterator.hasNext());
System.out.println(iterator.currentItem());
System.out.println(iterator.next());
System.out.println(iterator.hasNext());
System.out.println(iterator.next());
}
//定义迭代器接口
public interface Iterator<T> {
T first();
T next();
boolean hasNext();
T currentItem();
}
//定义具体迭代器
public static class ConcreteIterator implements Iterator<String> {
private List<String> list = new ArrayList<>();
private int index = 0;
public ConcreteIterator(List<String> list){
this.list = list;
}
@Override
public String first() {
index = 0;
return list.get(index);
}
@Override
public String next() {
if(hasNext()){
return list.get(++index);
}
return null;
}
@Override
public boolean hasNext() {
return index < list.size()-1;
}
@Override
public String currentItem() {
return list.get(index);
}
}
//定义集合接口
public static abstract class Aggregate {
public abstract Iterator<String> createIterator();
}
//定义具体集合
public static class ConcreteAggregate extends Aggregate {
private List<String> list = new ArrayList<>();
public ConcreteAggregate(List<String> list){
this.list = list;
}
@Override
public Iterator<String> createIterator() {
return new ConcreteIterator(list);
}
}
}
输出结果
a
b
true
b
c
false
null
使用内部类实现迭代器
在前图所示的迭代器模式结构图中可以看到,具体迭代器类和具体聚合类之间存在双重关系,其中一个关系为关联关系。在具体迭代器中需要维持一个对具体聚合对象的引用,该关联关系的目的是访问存储在聚合对象中的数据,以便迭代器能够对这些数据进行遍历操作。
除了使用关联关系外,为了能够让迭代器可以访问到聚合对象中的数据,还可以将迭代器类设计为聚合类的内部类。JDK中的迭代器类就是通过这种方法来实现的。
其中AbstractList类的代码片段如下:
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
...
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
/**
* Index of element to be returned by subsequent call to next.
*/
int cursor = 0;
/**
* Index of element returned by most recent call to next or
* previous. Reset to -1 if this element is deleted by a call
* to remove.
*/
int lastRet = -1;
/**
* The modCount value that the iterator believes that the backing
* List should have. If this expectation is violated, the iterator
* has detected concurrent modification.
*/
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size();
}
public E next() {
checkForComodification();
try {
int i = cursor;
E next = get(i);
lastRet = i;
cursor = i + 1;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException(e);
}
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
}
简单代码使用内部类实现
package behavior;
import java.util.List;
public class IteratorDemo {
public static void main(String[] args) {
ConcreteAggregate aggregate = new ConcreteAggregate(List.of("a", "b", "c"));
Iterator<String> iterator = aggregate.createIterator();
System.out.println(iterator.first());
System.out.println(iterator.next());
System.out.println(iterator.hasNext());
System.out.println(iterator.currentItem());
System.out.println(iterator.next());
System.out.println(iterator.hasNext());
System.out.println(iterator.next());
}
public interface Iterator<T> {
T first();
T next();
boolean hasNext();
T currentItem();
}
public abstract static class Aggregate {
public abstract Iterator<String> createIterator();
}
public static class ConcreteAggregate extends Aggregate {
private final List<String> list;
public ConcreteAggregate(List<String> list) {
this.list = list;
}
@Override
public Iterator<String> createIterator() {
return new ConcreteIterator();
}
private class ConcreteIterator implements Iterator<String> {
private int index = 0;
@Override
public String first() {
index = 0;
return list.get(index);
}
@Override
public String next() {
if (hasNext()) {
return list.get(++index);
}
return null;
}
@Override
public boolean hasNext() {
return index < list.size() - 1;
}
@Override
public String currentItem() {
return list.get(index);
}
}
}
}
JDK
内置的迭代器
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
总结
迭代器模式是一种使用频率非常高的设计模式,通过引入迭代器可以将数据的遍历功能从聚合对象中分离出来。聚合对象只负责存储数据,而遍历数据由迭代器来完成。由于很多编程语言的类库都已经实现了迭代器模式,因此在实际开发中,只需要直接使用Java、C#等语言已定义好的迭代器即可。迭代器已经成为操作聚合对象的基本工具之一。
主要优点
迭代器模式的主要优点如下:
(1)支持以不同的方式遍历一个聚合对象,在同一个聚合对象上可以定义多种遍历方式。在迭代器模式中只需要用一个不同的迭代器来替换原有迭代器即可改变遍历算法,也可以自己定义迭代器的子类以支持新的遍历方式。
(2)迭代器简化了聚合类。由于引入了迭代器,在原有的聚合对象中不需要再自行提供数据遍历等方法,这样可以简化聚合类的设计。
(3)在迭代器模式中,由于引入了抽象层,增加新的聚合类和迭代器类都很方便,无须修改原有代码,满足开闭原则的要求。
主要缺点
迭代器模式的主要缺点如下:
(1)由于迭代器模式将存储数据和遍历数据的职责分离,增加新的聚合类需要对应增加新的迭代器类,类的个数成对增加,这在一定程度上增加了系统的复杂性。
(2)抽象迭代器的设计难度较大,需要充分考虑到系统将来的扩展。例如JDK内置迭代器Iterator就无法实现逆向遍历,如果需要实现逆向遍历,只能通过其子类ListIterator等来实现,而ListIterator迭代器无法用于操作Set类型的聚合对象。在自定义迭代器时,创建一个考虑全面的抽象迭代器并不是件很容易的事情。
适用场景
在以下情况下可以考虑使用迭代器模式:
(1)访问一个聚合对象的内容而无须暴露它的内部表示。将聚合对象的访问与内部数据的存储分离,使得访问聚合对象时无须了解其内部实现细节。
(2)需要为一个聚合对象提供多种遍历方式。
(3)为遍历不同的聚合结构提供一个统一的接口,在该接口的实现类中为不同的聚合结构提供不同的遍历方式,而客户端可以一致性地操作该接口。
🚀 作者简介:作为某云服务提供商的后端开发人员,我将在这里与大家简要分享一些实用的开发小技巧。在我的职业生涯中积累了丰富的经验,希望能通过这个博客与大家交流、学习和成长。技术栈:Java、Golang、PHP、Python、Vue、React
本文收录于三木的
💐 「设计模式」专栏
此外三木还有以下专栏在同步更新~
🌼 「AI」专栏
🔥「面试」这个专栏的灵感来自于许多粉丝私信,大家向我咨询有关面试的问题和建议。我深感荣幸和责任,希望通过这个专栏,能够为大家提供更多关于面试的知识、技巧和经验。我们将一起探讨面试。期待粉丝们ssp的offer喜讯。
🎈 「Java探索者之路」系列专栏,这个专栏旨在引领Java开发者踏上一段真正探索Java世界的旅程。
我们将深入探讨Java编程的方方面面,从基础知识到高级技巧,从实践案例到最新趋势,帮助你成为一名卓越的Java探索者。如果有想进入Java后端领域工作的同学,这个专栏会对你有所帮助,欢迎关注起来呀
🌊 「Python爬虫」的入门学习系列,大家有兴趣的可以看一看
🌹一起学习,互三互访,顺评论区有访必回,有关必回!!!