`
lichaozhangobj
  • 浏览: 98850 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

迭代子模式

阅读更多

迭代子模式可以顺序地访问一个聚集中的元素而不必暴露聚集的内部表象,就是为不同的聚集提供相同的遍历方式,使得客户端只需要关注迭代接口,而不需要关注所迭代的聚集的具体结构

聚集和Java聚集
多个对象聚在一起形成的总体称之为聚集(Aggregate),聚集对象是能够包容一组对象的容器对象。聚集依赖于聚集结构的抽象化,具有复杂性和多样性,数组就是最基本的聚集,也是其他Java聚集对象的设计基础。

Java聚集(Collection)对象是实现了共同的java.util.Collection接口的对象,是Java语言对聚集的概念的直接支持。

为什么聚集需要迭代子
聚集对象必须提供适当的方法,允许客户端按照一个线性顺序遍历所有元素对象 ,把元素对象提取出来或者删除掉等。一个使用聚集的系统必然会使用这些方法操作聚集对象,因而在使用聚聚的系统演化过程中,会出现两类情况。
1.迭代逻辑没有改变,但是需要将一种聚集对象换成另一种聚集,因为不同的聚集具有不同的遍历接口,所以需要修改客户端代码,以便将已有的迭代调用换成新聚集对象所要求的接口。

2.聚集不改变,但是迭代方式需要改变,比如原来只需要读取元素和删除元素,但现在需要增加新的;或者原来的迭代仅仅遍历所有的元素,而现在则需要对元素加以过滤等。这时就只好修改聚集对象,修改已有的遍历方法,或者增加新的方法。

显然,出现这种情况是因为所涉及的聚集设计不符合“开-闭”原则,也就是因为没有将不变的结构从系统中抽象出来,与可变成分分割,并将可变部分的各种实现封装起来。一个聪明的做法无疑是应当使用更加抽象的处理方法,使得在进行迭代时,客户端根本无需知道所使用的聚集是哪个类型;而当客户端需要使用全新的迭代逻辑时,只需要引进一个新的迭代子对象即可,根本无需修改聚集对象本身。

迭代子模式模式便是这样的一个抽象化的概念,这一模式之所以能够做到这一点,是因为它将迭代逻辑封装到一个独立的迭代子对象汇总,从而与聚集本身分隔开。迭代子对象是对遍历的抽象化,不同的聚集对象可以提供相同的迭代子对象,从而使客户端无需知道聚集的底层结构,一个聚集可以提供多个不同的迭代子对象,从而使得遍历逻辑的变化不会影响到聚集对象本身。
 

宽接口和窄接口

如果一个聚集的接口提供了可以用来修改聚集元素的方法,这个接口就是所谓的宽接口;如果一个聚集的接口没有提供修改聚集元素的方法,这样的接口就是所谓的窄接口。

如果聚集对象为所有对象提供同一个接口,也就是宽接口的话,当然会满足迭代子模式对迭代子对象的要求。但是,这样会破坏对聚集对象的封装。这种提供宽接口的聚集叫做白箱聚集。聚集对象向外界提供同样的宽接口。

由于聚集自己实现迭代逻辑,并向外部提供适当的接口,使得子可以从外部控制聚集元素的迭代过程。这样一来迭代子所控制的仅仅是一个游标而已,这种迭代子叫做游标迭代子(Cursor Iterator)。

由于迭代子时在聚集的结构之外的,因此这样的迭代子又叫外禀迭代子(Extrinsic Iterator)

在改良的设计中,聚集对象为迭代子对象提供一个宽接口,而为其他对象提供一个窄接口。换言之,聚集对象的内部结构应当对迭代子对象适当公开,以便迭代子对象能够对聚集对像有足够的了解,从而可以进行迭代操作。但是,聚集对象应当避免向其他的对象提供这些方法,因为其他对象应当经过迭代子对象进行这些工作,而不是直接操控聚集对象。在备忘录模式中,有类似的双重接口的做法。

在Java语言中,实现双重接口的办法就是将迭代子类设计成聚集类的内部成员类。这样迭代子对象将可以像聚集对象的内部成员一样访问聚集对象的内部结构。这种同时保证聚集对象的封装和迭代子功能的实现的方案叫做暗箱实现方案。

由于迭代子时聚集的内部类,迭代子可以自由访问聚集的元素,所以迭代子可以自行实现迭代功能并控制对聚集元素的迭代逻辑。由于迭代子是在聚集的结构之内定义的,因此这样的迭代子又叫内禀迭代子(Intrinsic Iterator)


内禀子:聚集本身不提供访问其内部元素的方法,只有通过聚集内部的迭代子来遍历聚集,这时迭代子是个内部类,是聚集的一部分。
外禀子:聚集本身提供访问其内部元素的方法,可以通过外部的迭代子来遍历聚集,这时迭代子是个外部类,只维持对聚集的一个引用。

白箱聚集与外禀迭代子
一个白箱聚集像外界提供访问自己内部元素的接口(称做遍历方法或者Traversing Method),从而使外禀迭代子可以通过聚集的遍历方法实现迭代功能。
因为迭代的逻辑时由聚集对象本身提供的,所以这样的外禀迭代子角色往往仅仅保持迭代的游标位置。
一个典型的由白箱聚集和外部迭代子组成的系统如下:在这个实现中具体迭代子角色是一个外部类,而具体聚集角色向外界提供遍历聚集元素的接口。

 

public abstract class Aggregate {

    /**
     * 工厂方法返回一个迭代子对象
     */
    public Iterator createIterator() {
        return null;
    }
}

 

 

public class ConcreteAggregate extends Aggregate {

    private Object[] objs = { "A", "B", "C", "D", "E" };
    /**
     * 工厂方法:返回一个迭代子对象
     */
    public Iterator createIterator() {
        return new ConcreteIterator(this);
    }

    /**
     * 取值方法:向外提供聚集元素
     */
    public Object getElement(int index) {
        if (index < objs.length) {
            return objs[index];
        } else {
            return null;
        }
    }

    /**
     * 取值方法:向外提供聚集的大小
     */
    public int size() {
        return objs.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;
        size = agg.size();
        index = 0;
    }

    /**
     * 迭代方法:移动到第一个元素
     */
    public void first() {
        index = 0;
    }

    /**
     * 迭代方法:移动到下一个元素
     */
    public void next() {
        if (index < size) {
            index++;
        }
    }

    /**
     * 迭代方法:返回当前元素
     */
    public Object currentItem() {
        return agg.getElement(index);
    }

    /**
     * 迭代方法:是否是最后一个元素
     */
    public boolean isDone() {
        return index >= size;
    }
}

 

public class Client {

    private Iterator it;
    private Aggregate agg = new ConcreteAggregate();

    public void operation() {
        it = agg.createIterator();
        while (!it.isDone()) {
            System.out.println(it.currentItem().toString());
            it.next();
        }
    }

    public static void main(String[] args) {
        Client client = new Client();
        client.operation();
    }
}

 

暗箱聚集和内禀迭代子
一个黑箱聚集不向外部提供遍历自己元素对象的接口,因此这些元素对象只可以被聚集内部成员访问,由于内禀迭代子恰好是聚集内部的成员子类,因此,内禀迭代子对象是可以访问聚聚的元素的。

 

public abstract class Aggregate {

    /**
     * 工厂方法返回一个迭代子对象
     */
    public Iterator createIterator() {
        return null;
    }
}

 

 

public class ConcreteAggregate extends Aggregate {

    private Object[] objs = { "A", "B", "C", "D", "E" };

    /**
     * 工厂方法:返回一个迭代子对象
     */
    public Iterator createIterator() {
        return new ConcreteIterator();
    }
    /**
     * 内部成员类:具体迭代子类
     */
    private class ConcreteIterator implements Iterator {

        private int currentIndex = 0;

        public Object currentItem() {
            return objs[currentIndex];
        }

        public void first() {
            currentIndex = 0;
        }

        public boolean isDone() {
            return currentIndex == objs.length;
        }

        public void next() {
            if (currentIndex < objs.length) {
                currentIndex++;
            }
        }
    }
}

 

 

public interface Iterator {
    /**
     * 迭代方法:移动到第一个元素
     */
    public void first();

    /**
     * 迭代方法:移动到下一个元素
     */
    public void next();

    /**
     * 迭代方法:是否是最后一个元素
     */
    public boolean isDone();
    /**
     * 迭代方法:返回当前元素
     */
    public Object currentItem();
}

 

 

public class Client {

    private Iterator it;
    private Aggregate agg = new ConcreteAggregate();

    public void operation() {
        it = agg.createIterator();
        while (!it.isDone()) {
            System.out.println(it.currentItem().toString());
            it.next();
        }
    }

    public static void main(String[] args) {
        Client client = new Client();
        client.operation();
    }
}

 

静态迭代子和动态迭代子

静态迭代子由聚集对象创建,并持有聚集对象的一个快照(snapshot),在产生后这个快照的内容就不再变化。客户端可以继续修改原聚集的内容,但是迭代子对象不会反映出聚集的新变化。

静态迭代子的好处是它的安全性和简易性,换言之,静态迭代子易于实现,不容易出现错误,但是静态迭代子将原聚集复制了一份,因此它的短处是对时间和内存资源的消耗。对大型聚集对象来说,使用静态迭代子不是一个合适的选择。

动态迭代子则与静态迭代子完全相反,在迭代子被产生之后,迭代子保持着对聚集元素的引用,因此任何对原聚集内容的修改都会在迭代子对象上反映出来。

完整的动态迭代子不容易实现,但是简化的动态迭代子并不难实现。大多数Java设计师遇到的迭代子都是这种简化的动态迭代子,为了说明什么是简化的动态迭代子,首先需要介绍一个新的概念:Fail Fast.

Fail Fast
如果当一个算法开始之后,它的运算环境发生变化,使得算法无法进行必要的调整时,这个算法就应当立即发出故障信号,这就是Fail Fast的含义。
如果聚集对象的元素在一个动态迭代子的迭代过程中发生变化时,迭代过程会受到影响而变得不能自恰,这时候,迭代子就应当立即抛出一个异常。这种迭代子就是实现了Fail Fast功能的迭代子。

过滤迭代子
过滤迭代子可以在扫过聚集元素的同时进行计算,以确定呈现给客户端的元素都是满足一个过滤条件的,或者这些元素已经经过了重新排列。

何时使用内禀迭代子和外禀迭代子
内禀迭代子是定义在聚集结构内部的迭代子,而外禀迭代子是定义在聚集结构外部的迭代子。Java语言的设计师在设计AbstractList类时,选择了使用内禀迭代子类,也即Itr.但同时这个类也向外部提供自己遍历方法,换言之,如果设计师使用AbstractList聚集,也同样可以定义自己的外禀迭代子。

一个外禀迭代子往往仅存储一个游标,因此如果有介个客户端同时进行迭代的话,那么可以使用几个外禀迭代子对象,由每一个迭代子对象光控制一个独立的游标,但是,外禀迭代子要求聚集对象向外界提供遍历方法,因此会破坏聚集的封装,如果某一个客户端可以修改聚集元素的话,迭代会给出不自恰的结果,甚至影响到系统其他部分的稳定性,造成系统的崩溃。使用外禀迭代子的一个重要理由是它可以被几个不同的方法和对象共同享用和控制。使用内部迭代子的优点是它不破坏对聚集的封装。

分享到:
评论

相关推荐

    IteratorPattern 迭代设计模式

    IteratorPattern 迭代设计模式

    AspectJ实现设计模式(五)—迭代子模式

    本文介绍使用AspectJ实现设计模式之迭代子模式,文章以一个购买商品的例子实现AspectJ版本的内禀迭代子。由于迭代子模式应用广泛,文章在此不再赘述模式的具体内容了,我使用具体的例子说明如何使用AspectJ来完成...

    java迭代子模式详解

    主要为大家详细介绍了java迭代子模式的相关资料,需要的朋友可以参考下

    Iterator Pattern(迭代模式)

    源代码(eclipse直接打开) 博文链接:https://futrueboy.iteye.com/blog/382010

    设计模式-C++

    创建型模式,共五种:工厂方法模式、抽象工厂模式、...行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

    java设计模式示例

    java设计模式示例 创建型模式(5种):工厂方法模式,...行为型模式(11种):策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

    基于Java实现的23种设计模式Demo,抽象工厂模式、建造模式、工厂方法模式、原型模式、单例模式、外观模式、适配器模式等

    分别为:抽象工厂模式、建造模式、工厂方法模式、原型模式、单例模式、外观模式、适配器模式、桥接模式、组合模式、装饰模式、享元模式、代理模式、命令模式、解释器模式、访问者模式、迭代子模式、中介者模式、备忘...

    设计模式的精简版

    java设计模式的精简版,创建型模式:工厂方法模式,抽象...行为型模式:策略模式,模板方法模式,观察者模式,迭代子模式,责任链模式,命令模式,备忘录模式,状态模式,访问者模式,中介者模式,解释器模式,共11种

    23种设计模式项目实例

    创建型模式,共五种:工厂方法模式、抽象工厂模式、...行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

    23种设计模式实例

    总体来说设计模式分为三大类: ...行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式例子

    23种设计模式demo

    java的设计模式大体上分为三大类: ...行为型模式(11种):策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

    23种JAVA设计模式和15种J2EE设计模式

    行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。 其实还有两类:并发型模式和线程池模式。

    23个设计模式完整DEMO

    C#版的23个设计模式完整DEMO,包括: ...行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

    05-容易被忽略的迭代器(1).html

    迭代子模式( Iterator ) 责任链模式( Chain of Responsibility ) 命令模式( Command ) 备忘录模式( Memento ) 状态模式( State ) 访问者模式( Visitor ) 中介者模式( Mediator ) 解释器模式( ...

    23种设计模式java源码

    行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。 其实还有两类:并发型模式和线程池模式。

    c#设计模式

    总体来说设计模式分为三大类: ...行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

    java常用23中设计模式

    行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。 其实还有两类:并发型模式和线程池模式。

    unity-23种常见设计模式unity版

    总体来说设计模式分为三大类: 创建型模式:共五种:...行为型模式:共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

    32设计模式.doc

    创建型模式,共五种:工厂方法模式、抽象工厂模式、...行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

    Java23种设计模式可直接运行Demo

    行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。 其实还有两类:并发型模式和线程池模式。

Global site tag (gtag.js) - Google Analytics