迭代器模式
1 迭代器模式介绍
迭代器模式是我们学习一个设计时很少用到的、但编码实现时却经常使用到的行为型设计模式。在绝大多数编程语言中,迭代器已经成为一个基础的类库,直接用来遍历集合对象。在平时开发中,我们更多的是直接使用它,很少会从零去实现一个迭代器。
迭代器模式(Iterator pattern)又叫游标(Cursor)模式,它的原始定义是:迭代器提供一种对容器对象中的各个元素进行访问的方法,而又不需要暴露该对象的内部细节。
在软件系统中,容器对象拥有两个职责: 一是存储数据,而是遍历数据.从依赖性上看,前者是聚合对象的基本职责.而后者是可变化的,又是可分离的.因此可以将遍历数据的行为从容器中抽取出来,封装到迭代器对象中,由迭代器来提供遍历数据的行为,这将简化聚合对象的设计,更加符合单一职责原则
2 迭代器模式原理
迭代器模式结构图
迭代器模式主要包含以下角色:
- 抽象集合(Aggregate)角色:用于存储和管理元素对象, 定义存储、添加、删除集合元素的功能,并且声明了一个createIterator()方法用于创建迭代器对象。
- 具体集合(ConcreteAggregate)角色:实现抽象集合类,返回一个具体迭代器的实例。
- 抽象迭代器(Iterator)角色:定义访问和遍历聚合元素的接口,通常包含 hasNext()、next() 等方法。
- hasNext()函数用于判断集合中是否还有下一个元素
- next() 函数用于将游标后移一位元素
- currentItem() 函数,用来返回当前游标指向的元素
- 具体迭代器(Concretelterator)角色:实现抽象迭代器接口中所定义的方法,完成对集合对象的遍历,同时记录遍历的当前位置。
3 迭代器模式实现
/**
* 迭代器接口
**/
public interface Iterator<E> {
//判断集合中是否有下一个元素
boolean hasNext();
//将游标后移一位元素
void next();
//返回当前游标指定的元素
E currentItem();
}
/**
* 具体迭代器
**/
public class ConcreteIterator<E> implements Iterator<E>{
private int cursor; //游标
private ArrayList<E> arrayList; //容器
public ConcreteIterator(ArrayList<E> arrayList) {
this.cursor = 0;
this.arrayList = arrayList;
}
@Override
public boolean hasNext() {
return cursor != arrayList.size();
}
@Override
public void next() {
cursor++;
}
@Override
public E currentItem() {
if(cursor >= arrayList.size()){
throw new NoSuchElementException();
}
return arrayList.get(cursor);
}
}
public class Test01 {
public static void main(String[] args) {
ArrayList<String> names = new ArrayList<>();
names.add("lisi");
names.add("zhangsan");
names.add("wangwu");
Iterator<String> iterator = new ConcreteIterator(names);
while(iterator.hasNext()){
System.out.println(iterator.currentItem());
iterator.next();
}
/**
* 使用ArrayList集合中的iterator()方法获取迭代器
* 将创建迭代器的方法放入集合容器中,这样做的好处是对客户端封装了迭代器的实现细节.
*/
java.util.Iterator<String> iterator1 = names.iterator();
while(iterator1.hasNext()){
System.out.println(iterator1.next());
iterator.next();
}
}
}
4 迭代器模式应用实例
为了帮助你更好地理解迭代器模式,下面我们还是通过一个简单的例子给大家演示一下
/**
* 抽象迭代器 IteratorIterator
**/
public interface IteratorIterator<E> {
void reset(); //重置为第一个元素
E next(); //获取下一个元素
E currentItem(); //检索当前元素
boolean hasNext(); //判断是否还有下一个元素存在
}
/**
* 抽象集合 ListList
**/
public interface ListList<E> {
//获取迭代器对象的抽象方法(面向接口编程)
IteratorIterator<E> Iterator();
}
/**
* 主题类
**/
public class Topic {
private String name;
public Topic(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
/**
* 具体迭代器
**/
public class TopicIterator implements IteratorIterator<Topic> {
//Topic数组
private Topic[] topics;
//记录存储位置
private int position;
public TopicIterator(Topic[] topics) {
this.topics = topics;
position = 0;
}
@Override
public void reset() {
position = 0;
}
@Override
public Topic next() {
return topics[position++];
}
@Override
public Topic currentItem() {
return topics[position];
}
@Override
public boolean hasNext() {
if(position >= topics.length){
return false;
}
return true;
}
}
/**
* 具体集合类
**/
public class TopicList implements ListList<Topic> {
private Topic[] topics;
public TopicList(Topic[] topics) {
this.topics = topics;
}
@Override
public IteratorIterator<Topic> Iterator() {
return new TopicIterator(topics);
}
}
public class Client {
public static void main(String[] args) {
Topic[] topics = new Topic[4];
topics[0] = new Topic("topic1");
topics[1] = new Topic("topic2");
topics[2] = new Topic("topic3");
topics[3] = new Topic("topic4");
TopicList topicList = new TopicList(topics);
IteratorIterator<Topic> iterator = topicList.Iterator();
while(iterator.hasNext()){
Topic t = iterator.next();
System.out.println(t.getName());
}
}
}
5 迭代器模式总结
- 迭代器的优点:
- 迭代器模式支持以不同方式遍历一个集合对象,在同一个集合对象上可以定义多种遍历方式. 在迭代器模式中只需要用一个不同的迭代器来替换原有的迭代器,即可改变遍历算法,也可以自己定义迭代器的子类以支持新的遍历方式.
- 迭代器简化了集合类。由于引入了迭代器,在原有的集合对象中不需要再自行提供数据遍历等方法,这样可以简化集合类的设计。
- 在迭代器模式中,由于引入了抽象层,增加新的集合类和迭代器类都很方便,无须修改原有代码,满足 “基于接口编程而非实现” 和 “开闭原则” 的要求。
- 迭代器的缺点:
- 由于迭代器模式将存储数据和遍历数据的职责分离,增加了类的个数,这在一定程度上增加了系统的复杂性。
- 抽象迭代器的设计难度较大,需要充分考虑到系统将来的扩展.`
- 使用场景
-
减少程序中重复的遍历代码
对于放入一个集合容器中的多个对象来说,访问必然涉及遍历算法。如果我们不将遍历算法封装到容器里(比如,List、Set、Map 等),那么就需要使用容器的人自行去实现遍历算法,这样容易造成很多重复的循环和条件判断语句出现,不利于代码的复用和扩展,同时还会暴露不同容器的内部结构。而使用迭代器模式是将遍历算法作为容器对象自身的一种“属性方法”来使用,能够有效地避免写很多重复的代码,同时又不会暴露内部结构。
-
当需要为遍历不同的集合结构提供一个统一的接口时或者当访问一个集合对象的内容而无须暴露其内部细节的表示时。
迭代器模式把对不同集合类的访问逻辑抽象出来,这样在不用暴露集合内部结构的情况下,可以隐藏不同集合遍历需要使用的算法,同时还能够对外提供更为简便的访问算法接口。