java设计模式----迭代器模式

  • 定义

在软件构建过程当中,集合对象内部结构经常变化各异,但对于这些集合对象,咱们但愿在不暴露其内部结构的同时,可让外部客户代码透明地访问其中包含的元素;同时这种“透明遍历”也为同一种算法在多种集合对象上进行操做提供了可能。
使用面向对象技术将这种遍历机制抽象为“迭代器对象”为“应对变化中的集合对象”提供了一种优雅的方式。
迭代子(Iterator)模式又叫游标(Cursor)模式,是对象的行为模式。迭代子模式能够顺序地访问一个汇集中的元素而没必要暴漏汇集的内部表象。java

  • 使用场景

         遍历一个容器对象时算法

  • 为何汇集须要迭代子

多个对象聚在一块儿造成的整体称之为汇集(Aggregate),汇集对象是可以包容一组对象的容器对象。汇集依赖于汇集结构的抽象化,具备复杂性和多样性,数组就是最基本的汇集,也是其余Java汇集对象的设计基础。
Java汇集(Collection)对象是实现了共同的java.util.Collection接口的对象,是Java语言对汇集的概念的直接支持。
汇集对象必须提供适当的方法,容许客户端按照一个线性顺序遍历全部元素对象 ,把元素对象提取出来或者删除掉等。一个使用汇集的系统必然会使用这些方法操做汇集对象,于是在使用聚聚的系统演化过程当中,会出现两类状况。
◆ 迭代逻辑没有改变,可是须要将一种汇集对象换成另外一种汇集,由于不一样的汇集具备不一样的遍历接口,因此须要修改客户端代码,以便将已有的迭代调用换成新汇集对象所要求的接口。
◆ 汇集不改变,可是迭代方式须要改变,好比原来只须要读取元素和删除元素,但如今须要增长新的;或者原来的迭代仅仅遍历全部的元素,而如今则须要对元素加以过滤等。这时就只好修改汇集对象,修改已有的遍历方法,或者增长新的方法。
显然,出现这种状况是由于所涉及的汇集设计不符合“开-闭”原则,也就是由于没有将不变的结构从系统中抽象出来,与可变成分分割,并将可变部分的各类实现封装起来。一个聪明的作法无疑是应当使用更加抽象的处理方法,使得在进行迭代时,客户端根本无需知道所使用的汇集是哪一个类型;而当客户端须要使用全新的迭代逻辑时,只须要引进一个新的迭代子对象便可,根本无需修改汇集对象自己。
迭代子模式模式即是这样的一个抽象化的概念,这一模式之因此可以作到这一点,是由于它将迭代逻辑封装到一个独立的迭代子对象汇总,从而与汇集自己分隔开。迭代子对象是对遍历的抽象化,不一样的汇集对象能够提供相同的迭代子对象,从而使客户端无需知道汇集的低层结构,一个汇集能够提供多个不一样的迭代子对象,从而使得遍历逻辑的变化不会影响到汇集对象自己。数组

迭代子模式有两种实现方式,分别是白箱汇集与外禀迭代子和黑箱汇集于内禀迭代子。ide

白箱汇集与外禀迭代子
若是一个汇集的接口提供了能够用来修改汇集元素的方法,这个接口就是所谓的宽接口。
若是汇集对象为全部对象提供同一个接口,也就是宽接口的话,固然会知足迭代子模式对迭代子对象的要求。可是,这样会破坏对汇集对象的封装。这种提供宽接口的汇集叫作白箱汇集。函数

因为汇集本身实现迭代逻辑,并向外部提供适当的接口,使得迭代子能够从外部控制汇集元素的迭代过程。这样一来迭代子所控制的仅仅是一个游标而已,这种迭代子叫作游标迭代子(Cursor Iterator)。因为迭代子是在汇集结构以外的,所以这样的迭代子又叫作外禀迭代子(Extrinsic Iterator)。this

实现
一个白箱汇集向外界提供访问本身内部元素的接口(称做遍历方法或者Traversing Method),从而使外禀迭代子能够经过汇集的遍历方法实现迭代功能。
由于迭代的逻辑是由汇集对象自己提供的,因此这样的外禀迭代子角色每每仅仅保持迭代的游标位置。spa

角色
抽象迭代子(Iterator)角色:此抽象角色定义出遍历元素所需的接口。
具体迭代子(ConcreteIterator)角色:此角色实现了Iterator接口,并保持迭代过程当中的游标位置。
汇集(Aggregate)角色:此抽象角色给出建立迭代子(Iterator)对象的接口。
具体汇集(ConcreteAggregate)角色:实现了建立迭代子(Iterator)对象的接口,返回一个合适的具体迭代子实例。
客户端(Client)角色:持有对汇集及其迭代子对象的引用,调用迭代子对象的迭代接口,也有可能经过迭代子操做汇集元素的增长和删除。设计

抽象汇集角色类
这个角色规定出全部的具体汇集必须实现的接口。迭代子模式要求汇集对象必须有一个工厂方法,也就是createIterator()方法,以向外界提供迭代子对象的实例。code

public abstract class Aggregate {
    /**
     * 工厂方法,建立相应迭代子对象的接口
     */
    public abstract Iterator createIterator();
}


具体汇集角色类
实现了抽象汇集角色类所要求的接口,也就是createIterator()方法。此外,还有方法getElement()向外界提供汇集元素,而方法size()向外界提供汇集的大小等。对象

public class ConcreteAggregate extends Aggregate {
    
    private Object[] objArray = null;
    /**
     * 构造方法,传入聚合对象的具体内容
     */
    public ConcreteAggregate(Object[] objArray){
        this.objArray = objArray;
    }
    
    @Override
    public Iterator createIterator() {
        
        return new ConcreteIterator(this);
    }
    /**
     * 取值方法:向外界提供汇集元素
     */
    public Object getElement(int index){
        
        if(index < objArray.length){
            return objArray[index];
        }else{
            return null;
        }
    }
    /**
     * 取值方法:向外界提供汇集的大小
     */
    public int size(){
        return objArray.length;
    }
}


抽象迭代子角色类

public interface Iterator {
    /**
     * 迭代方法:移动到第一个元素
     */
    public void first();
    /**
     * 迭代方法:移动到下一个元素
     */
    public void next();
    /**
     * 迭代方法:是否为最后一个元素
     */
    public boolean isDone();
    /**
     * 迭代方法:返还当前元素
     */
    public Object currentItem();
}


具体迭代子角色类

public class ConcreteIterator implements Iterator {
    //持有被迭代的具体的聚合对象
    private ConcreteAggregate agg;
    //内部索引,记录当前迭代到的索引位置
    private int index = 0;
    //记录当前汇集对象的大小
    private int size = 0;
    
    public ConcreteIterator(ConcreteAggregate agg){
        this.agg = agg;
        this.size = agg.size();
        index = 0;
    }
    /**
     * 迭代方法:返还当前元素
     */
    @Override
    public Object currentItem() {
        return agg.getElement(index);
    }
    /**
     * 迭代方法:移动到第一个元素
     */
    @Override
    public void first() {
        
        index = 0;
    }
    /**
     * 迭代方法:是否为最后一个元素
     */
    @Override
    public boolean isDone() {
        return (index >= size);
    }
    /**
     * 迭代方法:移动到下一个元素
     */
    @Override
    public void next() {
 
        if(index < size)
        {
            index ++;
        }
    }
 
}


客户端类

public class Client {
 
    public void operation(){
        Object[] objArray = {"One","Two","Three","Four","Five","Six"};
        //建立聚合对象
        Aggregate agg = new ConcreteAggregate(objArray);
        //循环输出聚合对象中的值
        Iterator it = agg.createIterator();
        while(!it.isDone()){
            System.out.println(it.currentItem());
            it.next();
        }
    }
    public static void main(String[] args) {
        
        Client client = new Client();
        client.operation();
    }
 
}


上面的例子首先建立了一个汇集类实例,而后调用汇集对象的工厂方法createIterator()以获得一个迭代子对象。在获得迭代子的实例后,客户端开始迭代过程,打印出全部的汇集元素。

意义
一个经常会问的问题是:既然白箱汇集已经向外界提供了遍历方法,客户端已经能够自行进行迭代了,为何还要应用迭代子模式,并建立一个迭代子对象进行迭代呢?
客户端固然能够自行进行迭代,不必定非得须要一个迭代子对象。可是,迭代子对象和迭代模式会将迭代过程抽象化,将做为迭代消费者的客户端与迭代负责人的迭代子责任分隔开,使得二者能够独立的演化。在汇集对象的种类发生变化,或者迭代的方法发生改变时,迭代子做为一个中介层能够吸取变化的因素,而避免修改客户端或者汇集自己。
此外,若是系统须要同时针对几个不一样的汇集对象进行迭代,而这些汇集对象所提供的遍历方法有所不一样时,使用迭代子模式和一个外界的迭代子对象是有意义的。具备同一迭代接口的不一样迭代子对象处理具备不一样遍历接口的汇集对象,使得系统可使用一个统一的迭代接口进行全部的迭代。

黑箱汇集与内禀迭代子
若是一个汇集的接口没有提供修改汇集元素的方法,这样的接口就是所谓的窄接口。
汇集对象为迭代子对象提供一个宽接口,而为其余对象提供一个窄接口。换言之,汇集对象的内部结构应当对迭代子对象适当公开,以便迭代子对象可以对汇集对象有足够的了解,从而能够进行迭代操做。可是,汇集对象应当避免向其余的对象提供这些方法,由于其余对象应当通过迭代子对象进行这些工做,而不是直接操控汇集对象。

在JAVA语言中,实现双重接口的办法就是将迭代子类设计成汇集类的内部成员类。这样迭代子对象将能够像汇集对象的内部成员同样访问汇集对象的内部结构。下面给出一个示意性的实现,说明这种双重接口的结构时怎么样产生的,以及使用了双重接口结构以后迭代子模式的实现方案。这种同时保证汇集对象的封装和迭代子功能的实现的方案叫作黑箱实现方案。
因为迭代子是汇集的内部类,迭代子能够自由访问汇集的元素,因此迭代子能够自行实现迭代功能并控制对汇集元素的迭代逻辑。因为迭代子是在汇集的结构以内定义的,所以这样的迭代子又叫作内禀迭代子(Intrinsic Iterator)。

应用
为了说明黑箱方案的细节,这里给出一个示意性的黑箱实现。在这个实现里,汇集类ConcreteAggregate含有一个内部成员类ConcreteIterator,也就是实现了抽象迭代子接口的具体迭代子类,同时汇集并不向外界提供访问本身内部元素的方法。

抽象汇集角色类
这个角色规定出全部的具体汇集必须实现的接口。迭代子模式要求汇集对象必须有一个工厂方法,也就是createIterator()方法,以向外界提供迭代子对象的实例。

public abstract class Aggregate {
    /**
     * 工厂方法,建立相应迭代子对象的接口
     */
    public abstract Iterator createIterator();
}


抽象迭代子角色类

public interface Iterator {
    /**
     * 迭代方法:移动到第一个元素
     */
    public void first();
    /**
     * 迭代方法:移动到下一个元素
     */
    public void next();
    /**
     * 迭代方法:是否为最后一个元素
     */
    public boolean isDone();
    /**
     * 迭代方法:返还当前元素
     */
    public Object currentItem();
}


具体汇集角色类
实现了抽象汇集角色所要求的接口,也就是createIterator()方法。此外,汇集类有一个内部成员类ConcreteIterator,这个内部类实现了抽象迭代子角色所规定的接口;而工厂方法createIterator()所返还的就是这个内部成员类的实例。

public class ConcreteAggregate extends Aggregate {
    
    private Object[] objArray = null;
    /**
     * 构造方法,传入聚合对象的具体内容
     */
    public ConcreteAggregate(Object[] objArray){
        this.objArray = objArray;
    }
    
    @Override
    public Iterator createIterator() {
 
        return new ConcreteIterator();
    }
    /**
     * 内部成员类,具体迭代子类
     */
    private class ConcreteIterator implements Iterator
    {
        //内部索引,记录当前迭代到的索引位置
        private int index = 0;
        //记录当前汇集对象的大小
        private int size = 0;
        /**
         * 构造函数
         */
        public ConcreteIterator(){
            
            this.size = objArray.length;
            index = 0;
        }
        /**
         * 迭代方法:返还当前元素
         */
        @Override
        public Object currentItem() {
            return objArray[index];
        }
        /**
         * 迭代方法:移动到第一个元素
         */
        @Override
        public void first() {
            
            index = 0;
        }
        /**
         * 迭代方法:是否为最后一个元素
         */
        @Override
        public boolean isDone() {
            return (index >= size);
        }
        /**
         * 迭代方法:移动到下一个元素
         */
        @Override
        public void next() {
 
            if(index < size)
            {
                index ++;
            }
        }
    }
}


客户端类

public class Client {
 
    public void operation(){
        Object[] objArray = {"One","Two","Three","Four","Five","Six"};
        //建立聚合对象
        Aggregate agg = new ConcreteAggregate(objArray);
        //循环输出聚合对象中的值
        Iterator it = agg.createIterator();
        while(!it.isDone()){
            System.out.println(it.currentItem());
            it.next();
        }
    }
    public static void main(String[] args) {
        
        Client client = new Client();
        client.operation();
    }
 
}


上面的例子首先建立了一个汇集类实例,而后调用汇集对象的工厂方法createIterator()以获得一个迭代子对象。在获得迭代子的实例后,客户端开始迭代过程,打印出全部的汇集元素。

迭代子模式优势 迭代子模式简化了汇集的接口。迭代子具有了一个遍历接口,这样汇集的接口就没必要具有遍历接口。 每个汇集对象均可以有一个或多个迭代子对象,每个迭代子的迭代状态能够是彼此独立的。所以,一个汇集对象能够同时有几个迭代在进行之中。 因为遍历算法被封装在迭代子角色里面,所以迭代的算法能够独立于汇集角色变化。

相关文章
相关标签/搜索