迭代器模式
提供一种方法来访问聚合对象,而不用暴露这个对象的内部表示,其别名为游标。迭代器模式使用对象行为型的模式。
(1)遍历和管理未作分离,导致聚合类的职责过重,既可以负责存储和管理数据,又负责遍历数据,违反了单一职责,给测试和维护增加难度。
(2)如果将抽象聚合类声明为一个接口,则这个接口充斥了大量的方法,不利于子类实现,违反了接口隔离原则。
(3)如果将所有的遍历操作交给了子类,将导致子类代码非常庞大,而且必须暴露AbstractObjectList的内部存储细节,向子类公开自己的属性,破坏了AbstractObjectList类的封装性。
解决方案
- 负责遍历的方法提取出来,封装到专门的类中,实现数据存储和遍历的分离,无须暴露聚合类的内部属性即可对其进行操作。
结构图
(1)Iterator(抽象迭代器,抽象产品)定义了访问和遍历元素的接口,声明遍历数据元素的方法。比如获取第一个元素first(),用于访问下一个元素的next()方法,用于判断是否还有下一个元素的hasNext()方法,用于获取当前元素的currentItem方法等,在具体迭代器中将实现这些方法
(2)ConcreteIterator(具体迭代器,具体产品)实现了抽象迭代器接口,完成对聚合对象的遍历,同时在具体迭代器用过游标来记录在聚合对象中所处的位置,具体实现时,游标通常是一个表示位置的非负整数。
(3)Aggregate(抽象聚合类,抽象工厂)它用于存储和管理元素,声明一个creteIterator方法用于创建一个迭代器对象,充当抽象迭代器工厂角色
(4)ConcreteAggregare(具体聚合类,具体工厂)实现了在抽象聚合类中申明的creteIterator方法,返回一个与改具体聚合类对应的具体迭代器实例。
迭代器模式提供了一个外部迭代器来对聚合对象的访问和遍历,定义了一个访问该聚合元素的接口,并且可以跟踪当前遍历的元素,了解哪些元素已经遍历过,哪些没有,迭代器的引入,将使得对一个复杂聚合对象的操作变得简单。
package com.learn.designmode.mode.iterator;
/**
* 抽象迭代器
*/
public interface Iterator {
/**
* 将游标指向第一个元素
*/
void first();
/**
* 将游标指向下一个元素
*/
void next();
/** 判断是否还有下一个元素
* @return
*/
boolean hasNext();
/** 将游标指向当前元素
* @return
*/
Object currentItem();
}
/**
* 具体迭代器
*/
class ConcreteIterator implements Iterator{
/**
* 维持对一个具体聚合类的引用,以便用于访问存储在聚合对象中的数据
*/
private Aggregate object;
/**
* 定义一个游标,用于访问当前的位置
*/
private int curror;
public ConcreteIterator(ConcreteAggregate concreteAggregate){
this.object = concreteAggregate;
}
@Override
public void first() {
}
@Override
public void next() {
}
@Override
public boolean hasNext() {
return false;
}
@Override
public Object currentItem() {
return null;
}
}
/**
* 抽象聚合类
*/
interface Aggregate{
Iterator createIterator();
}
/**
* 具体聚合类
*/
class ConcreteAggregate implements Aggregate{
@Override
public Iterator createIterator() {
return new ConcreteIterator(this);
}
}
答:假如具体聚合有一个List集合,这个类还有一个生成具体迭代器的方法,说明了使用具体迭代器可以操作集合,是通过将这个具体聚合类传入迭代器中实现的。
完整的解决方案
package com.learn.designmode.mode.iterator.demo;
import java.util.List;
/**
* 抽象迭代器
*/
public interface AbstractIteraotr {
/**
* 是否第一个元素
*/
boolean isFirst();
/**
* 判读是否为最后一个元素
*/
boolean isLast();
/**
* 移植到上一个元素
*/
void previous();
/**
* 将游标指向下一个元素
*/
void next();
/** 判断是否还有下一个元素
* @return
*/
boolean hasNext();
/** 获取下一个元素
* @return
*/
Object getNextItem();
/** 获取上一个元素
* @return
*/
Object getPreviousItem();
}
/**
* 具体迭代器(商品迭代器)
*/
class ConcreteIteraotr implements AbstractIteraotr{
/**
* 维持对一个具体聚合类的引用
*/
private ProductList productList;
private List<ProductList> productLists;
/**
* 设置正向遍历游标的初始值
*/
private int curror1;
/**
* 设置逆向遍历游标的初始值
*/
private int curror2;
// 传入具体聚合类
public ConcreteIteraotr(ProductList list){
this.productList = list;
this.productLists = list.getObjects();
curror1 = 0;
// 为-1表示无集合
curror2 = this.productLists.size() - 1;
}
@Override
public boolean isFirst() {
return curror2 == -1;
}
@Override
public boolean isLast() {
return this.curror1 == productLists.size() ;
}
@Override
public void previous() {
if (curror2 > -1){
curror2 -- ;
}
}
@Override
public void next() {
if (curror1 < productLists.size()){
curror1 ++ ;
}
}
@Override
public boolean hasNext() {
return false;
}
@Override
public Object getNextItem() {
return productLists.get(curror1);
}
@Override
public Object getPreviousItem() {
return productLists.get(curror2);
}
}
package com.learn.designmode.mode.iterator.demo;
import java.util.ArrayList;
import java.util.List;
/**
* 抽象聚合类
*/
public abstract class AbstractObjectList {
protected List<Object> objectList = new ArrayList<Object>();
public AbstractObjectList(List objectList){
this.objectList = objectList;
}
public void addObject(Object object){
objectList.add(object);
}
public void removeObject(Object object){
objectList.remove(object);
}
public List getObjects(){
return objectList;
}
/**
* 声明创建迭代器对象的抽象工厂方法
*/
public abstract AbstractIteraotr createIterator();
}
/**
* 商品数据类,具体聚合类
*/
class ProductList extends AbstractObjectList{
public ProductList(List objectList) {
super(objectList);
}
@Override
public AbstractIteraotr createIterator() {
return new ConcreteIteraotr(this);
}
}
package com.learn.designmode.mode.iterator.demo;
import java.util.ArrayList;
import java.util.List;
public class Client {
public static void main(String[] args) {
List lists = new ArrayList();
lists.add("倚天剑");
lists.add("屠龙刀");
lists.add("断肠草");
lists.add("葵发宝典");
lists.add("四十二章经");
// 具体聚合类
AbstractObjectList objectList = new ProductList(lists);
AbstractIteraotr iteraotr = objectList.createIterator();
System.out.println("正向遍历");
while (!iteraotr.isLast()){
System.out.println(iteraotr.getNextItem());
iteraotr.next();
}
System.out.println("逆向遍历");
while (!iteraotr.isFirst()){
System.out.println(iteraotr.getPreviousItem());
iteraotr.previous();
}
}
}
使用内部类实现迭代器
上面我们可以看到,具体迭代器和具体聚合类存在双重关系,一个为关联关系,具体迭代器需要维持一个对具体聚合对象的引用,关联的目标在于能够访问对象中的数据,以便遍历
除了关联关系以外,我们可以将迭代器设计为聚合类的内部类,JDK的迭代器类就是通过这种方法实现的
修改ProdList类,将具体迭代器类作为具体聚合类的内部类
/**
* 商品数据类,具体聚合类
*/
class ProductList extends AbstractObjectList{
public ProductList(List objectList) {
super(objectList);
}
@Override
public AbstractIteraotr createIterator() {
return new ConcreteIteraotr(this);
}
/**
* 具体迭代器(商品迭代器)
*/
private class ConcreteIteraotr implements AbstractIteraotr{
/**
* 维持对一个具体聚合类的引用
*/
private ProductList productList;
private List<ProductList> productLists;
/**
* 设置正向遍历游标的初始值
*/
private int curror1;
/**
* 设置逆向遍历游标的初始值
*/
private int curror2;
// 传入具体聚合类
public ConcreteIteraotr(ProductList list){
this.productList = list;
this.productLists = list.getObjects();
curror1 = 0;
// 为-1表示无集合
curror2 = this.productLists.size() - 1;
}
@Override
public boolean isFirst() {
return curror2 == -1;
}
@Override
public boolean isLast() {
return this.curror1 == productLists.size() ;
}
@Override
public void previous() {
if (curror2 > -1){
curror2 -- ;
}
}
@Override
public void next() {
if (curror1 < productLists.size()){
curror1 ++ ;
}
}
@Override
public boolean hasNext() {
return false;
}
@Override
public Object getNextItem() {
return productLists.get(curror1);
}
@Override
public Object getPreviousItem() {
return productLists.get(curror2);
}
}
}
JDK内置迭代器
因为Iterator接口只有三个定义的方法next remove hasNext,并没有逆向的方法。ListIterator只是对其的加强。
总结
迭代器模式主要用于遍历跟存储管理分离,由于很多代码语言库实现了迭代器模式,可以直接使用。
优点
(1)支持以不同的方式遍历一个聚合对象,迭代器模式只需要一个不同的迭代器来替换原有的迭代器即可改变遍历算法,符合开闭原则。
(2)迭代器简化了聚合类,由于引入了迭代器,在原有的聚合对象中不需要自行提供数据遍历的方法。
(3)在迭代器模式中,由于引入了抽象层,新增迭代器和聚合类都不需要修改原有的代码。
缺点
(1)系统中类的个数成对增加,具体聚合类对应具体迭代器。
(2)设计难度较大,需要充分考虑到系统将来的扩展,比如JDK的内置迭代器Iterator无法实现逆向遍历,只能通过其子类ListIterator等来实现,而它又不可以操作Set类型的聚合对象,在自定义迭代器时,创建一个考虑全面的抽象迭代器不是一件容易的事情。
适用场景
(1)访问一个聚合对象的内容无须暴露它的内部表示,将聚合对象的访问与内部数据存储分离,使得访问聚合对象无须了解其内部实现细节。
(2)需要为一种聚合对象提供多种遍历方式
(3)为遍历不同的聚合结构提供统一的接口,在该接口的实现类中为不同的聚合结构提供不同遍历方式,而客户端可以一致性地操作该接口。