介绍
一个容器,必然会涉及到遍历算法,如果将它封装在容器中,则增加了复杂度,如果客户实现,则不那么方便。所以引入第三方类来帮忙处理,这个类就是迭代器。
又称为游标模式,行为型设计模式之一。我的理解是目的在遍历,弱化容器与遍历算法的关系。在容器与访问类中添加一个迭代器,用于承担遍历,又不需要访问类自行实现。提供一种顺序访问一个容器对象中的各个元素,而又不需要暴露改对象的内部表示。
Android中数据库cusor则使用到了该模式。各个语言一般都有各自的迭代器实现,开发中很少会自己去实现迭代器了。
UML
组成结构
- Iterator:迭代器接口,负责定义、访问、遍历元素的接口
- ConcreteIterator:具体迭代器类,实现迭代器接口,并记录当前位置。
- Aggregate:容器接口,负责提供创建迭代器角色的接口
- ConcreteAggregate:具体容器类,具体迭代器角色与该容易相关联
使用场景
- 遍历一个容器对象时
事例
比如有两个北鼻,他们喜欢的水果不同,咱们想遍历问问她们喜欢的水果,先看看不用迭代器的方式:
- 建立一个水果对象,用于承载。简易北鼻1和北鼻2对象,她们其中都有自己喜欢的水果列表持有,一个是以数组方式,一个是以列表方式,并提供获取数组和列表的方法:
/**
* 水果
*/
public class ITeFruit {
/**
* 水果名称
*/
private String name;
public ITeFruit(String name) {
this.name = name;
}
@Override
public String toString() {
return "ITeFruit{" +
"name='" + name + '\'' +
'}';
}
}
/**
* 北鼻1水果
*/
public class Beibi1Fruit {
private List<ITeFruit> iTeFruits = new ArrayList<>();
public Beibi1Fruit() {
iTeFruits.add(new ITeFruit("菠萝"));
iTeFruits.add(new ITeFruit("香蕉"));
iTeFruits.add(new ITeFruit("哈密瓜"));
iTeFruits.add(new ITeFruit("榴莲"));
iTeFruits.add(new ITeFruit("火龙果"));
}
public List<ITeFruit> getiTeFruits(){
return iTeFruits;
}
}
/**
* 北鼻二喜欢的水果
*/
public class Beibi2Fruit {
private ITeFruit[] beibi2 = new ITeFruit[4];
public Beibi2Fruit() {
beibi2[0] = new ITeFruit("苹果");
beibi2[1] = new ITeFruit("梨子");
beibi2[2] = new ITeFruit("芒果");
beibi2[3] = new ITeFruit("哈密瓜");
}
/**
* 获取北鼻二喜欢的水果数组
*
* @return 水果数组
*/
public ITeFruit[] getBeibi2Fruits() {
return this.beibi2;
}
}
- 进行遍历查询(这个没用增强for循环,那样看就没区别了):
//北鼻1,遍历
Beibi1Fruit beibi1Fruit = new Beibi1Fruit();
List<ITeFruit> iTeFruits = beibi1Fruit.getiTeFruits();
System.out.println("北鼻一喜欢水果:");
for (int i = 0; i < iTeFruits.size(); i++) {
System.out.println(iTeFruits.get(i));
}
//北鼻2,遍历
Beibi2Fruit beibi2Fruit = new Beibi2Fruit();
ITeFruit[] beibi2Fruits = beibi2Fruit.getBeibi2Fruits();
System.out.println("北鼻二喜欢水果:");
for (int i = 0; i < beibi2Fruits.length; i++) {
System.out.println(beibi2Fruits[i]);
}
输出就不贴了,从这里就能看到,遍历两个的水果,使用的方式是不一样的,那么对外界来说得知道每个的遍历方式,相对麻烦了,那么就引入了,提供一个迭代器来实现。
- 建立一个迭代器接口,提供获取下一对象和是否还有下一个的方法:
/**
* 迭代器接口
*/
public interface IteratorF {
/**
* 是否还有下一个
*
* @return
*/
boolean hasNext();
/**
* 下一个
*
* @return 有则返回下一个对象,否则返回null
*/
Object next();
}
- 创建北鼻1和北鼻2的迭代器,其中各自实现迭代方法:
/**
* 北鼻1的迭代器
*/
public class Beibi1Iterator implements IteratorF {
private List<ITeFruit> iTeFruits = new ArrayList<>();
private int position;
public Beibi1Iterator(List<ITeFruit> iTeFruits) {
this.iTeFruits = iTeFruits;
}
@Override
public boolean hasNext() {
//如果列表中有个数且当前position还没到末尾
return (iTeFruits.size() > 0 && position <= iTeFruits.size() - 1);
}
@Override
public Object next() {
//获取一个水果,然后将position+1
return iTeFruits.get(position++);
}
}
/**
* 北鼻2的迭代器
*/
public class Beibi2Iterator implements IteratorF {
private ITeFruit[] beibi2 = new ITeFruit[4];
private int position;
public Beibi2Iterator(ITeFruit[] beibi2) {
this.beibi2 = beibi2;
}
@Override
public boolean hasNext() {
//如果数组中有个数且当前position还没到末尾
return (beibi2.length > 0 && position <= beibi2.length - 1);
}
@Override
public Object next() {
//获取一个水果,然后将position+1
return beibi2[position++];
}
}
- 创建容器接口,提供一个获取迭代器的方法(这里简单点,就只提供下获取迭代器):
/**
* 容器接口,该接口返回一个迭代器,由容器来实现
*/
public interface Aggregate {
/**
* 返回一个迭代器
*
* @return 迭代器
*/
IteratorF iterator();
}
- 容器实现迭代器方法:
/**
* 北鼻1水果
*/
public class Beibi1Fruit implements Aggregate {
private List<ITeFruit> iTeFruits = new ArrayList<>();
public Beibi1Fruit() {
iTeFruits.add(new ITeFruit("菠萝"));
iTeFruits.add(new ITeFruit("香蕉"));
iTeFruits.add(new ITeFruit("哈密瓜"));
iTeFruits.add(new ITeFruit("榴莲"));
iTeFruits.add(new ITeFruit("火龙果"));
}
/**
* 创建一个迭代器并返回
*
* @return
*/
@Override
public IteratorF iterator() {
return new Beibi1Iterator(this.iTeFruits);
}
}
/**
* 北鼻二喜欢的水果
*/
public class Beibi2Fruit implements Aggregate {
private ITeFruit[] beibi2 = new ITeFruit[4];
public Beibi2Fruit() {
beibi2[0] = new ITeFruit("苹果");
beibi2[1] = new ITeFruit("梨子");
beibi2[2] = new ITeFruit("芒果");
beibi2[3] = new ITeFruit("哈密瓜");
}
@Override
public IteratorF iterator() {
return new Beibi2Iterator(this.beibi2);
}
}
- 遍历:
//北鼻1,遍历
Beibi1Fruit beibi1Fruit = new Beibi1Fruit();
IteratorF iterator1 = beibi1Fruit.iterator();
System.out.println("北鼻一喜欢水果:");
while (iterator1.hasNext()) {
System.out.println(iterator1.next());
}
//北鼻2,遍历
Beibi2Fruit beibi2Fruit = new Beibi2Fruit();
IteratorF iterator2 = beibi2Fruit.iterator();
System.out.println("北鼻二喜欢水果:");
while (iterator2.hasNext()) {
System.out.println(iterator2.next());
}
输出和第一步是一样的,但是看到遍历的地方了吗?这样子遍历就完全一样了,对使用方来说就非常方便了。
优缺点
优点
- 自身优点单一,非常满足单一职责原则。
缺点
- 对类文件的增加
总结:这个设计模式虽然平时我们一般比较少主动去使用,是因为库中已经有了,但是它遍历的设计思想,提供给客户的方便,内部的单一职责原则,是非常值得借鉴的。