迭代子模式

概述

概念:在阎宏博士的《JAVA与模式》中关于迭代子模式的定义是这样的:迭代子模式又叫游标(Cursor)模式,是对象的行为模式。迭代子模式可以顺序地访问一个聚集中的元素而不必暴露聚集的内部表象(internal representation)。

迭代子模式的意图及组成

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

白箱聚集与外禀迭代子

如果一个聚集的接口提供了可以用来修改聚集元素的方法,这个接口就是所谓的宽接口。 如果聚集对象为所有对象提供同一个接口,也就是宽接口的话,当然会满足迭代子模式对迭代子对象的要求。但是,这样会破坏对聚集对象的封装。这种提供宽接口的聚集叫做白箱聚集。聚集对象向外界提供同样的宽接口,如下图所示:

由于聚集自己实现迭代逻辑,并向外部提供适当的接口,使得迭代子可以从外部控制聚集元素的迭代过程。这样一来迭代子所控制的仅仅是一个游标而已,这种迭代子叫做游标迭代子(Cursor Iterator)。由于迭代子是在聚集结构之外的,因此这样的迭代子又叫做外禀迭代子(Extrinsic Iterator)。 一个白箱聚集向外界提供访问自己内部元素的接口(称作遍历方法或者Traversing Method),从而使外禀迭代子可以通过聚集的遍历方法实现迭代功能。   因为迭代的逻辑是由聚集对象本身提供的,所以这样的外禀迭代子角色往往仅仅保持迭代的游标位置。   一个典型的由白箱聚集与外禀迭代子组成的系统如下图所示:

迭代子模式组成

迭代子模式的组成主要有以下几个角色: 抽象迭代子(Iterator)角色:此抽象角色定义出遍历元素所需的接口。 具体迭代子(ConcreteIterator)角色:此角色实现了Iterator接口,并保持迭代过程中的游标位置。 聚集(Aggregate)角色:此抽象角色给出创建迭代子(Iterator)对象的接口。 具体聚集(ConcreteAggregate)角色:实现了创建迭代子(Iterator)对象的接口,返回一个合适的具体迭代子实例。 客户端(Client)角色:持有对聚集及其迭代子对象的引用,调用迭代子对象的迭代接口,也有可能通过迭代子操作聚集元素的增加和删除。

代码实例

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

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();  
    }  

} 

黑箱聚集与内禀迭代子

如果一个聚集的接口没有提供修改聚集元素的方法,这样的接口就是所谓的窄接口。   聚集对象为迭代子对象提供一个宽接口,而为其他对象提供一个窄接口。换言之,聚集对象的内部结构应当对迭代子对象适当公开,以便迭代子对象能够对聚集对象有足够的了解,从而可以进行迭代操作。但是,聚集对象应当避免向其他的对象提供这些方法,因为其他对象应当经过迭代子对象进行这些工作,而不是直接操控聚集对象。

在JAVA语言中,实现双重接口的办法就是将迭代子类设计成聚集类的内部成员类。这样迭代子对象将可以像聚集对象的内部成员一样访问聚集对象的内部结构。下面给出一个示意性的实现,说明这种双重接口的结构时怎么样产生的,以及使用了双重接口结构之后迭代子模式的实现方案。这种同时保证聚集对象的封装和迭代子功能的实现的方案叫做黑箱实现方案。   由于迭代子是聚集的内部类,迭代子可以自由访问聚集的元素,所以迭代子可以自行实现迭代功能并控制对聚集元素的迭代逻辑。由于迭代子是在聚集的结构之内定义的,因此这样的迭代子又叫做内禀迭代子(Intrinsic Iterator)。   为了说明黑箱方案的细节,这里给出一个示意性的黑箱实现。在这个实现里,聚集类ConcreteAggregate含有一个内部成员类ConcreteIterator,也就是实现了抽象迭代子接口的具体迭代子类,同时聚集并不向外界提供访问自己内部元素的方法。

代码实例

抽象聚集角色类

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

抽象迭代子角色类

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

具体聚集角色类,实现了抽象聚集角色所要求的接口。

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();
    }

}

使用迭代子模式的优点

 (1)迭代子模式简化了聚集的接口。迭代子具备了一个遍历接口,这样聚集的接口就不必具备遍历接口。   (2)每一个聚集对象都可以有一个或多个迭代子对象,每一个迭代子的迭代状态可以是彼此独立的。因此,一个聚集对象可以同时有几个迭代在进行之中。   (3)由于遍历算法被封装在迭代子角色里面,因此迭代的算法可以独立于聚集角色变化。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏专注数据中心高性能网络技术研发

[Effective Modern C++(11&14)]Chapter 3: Moving to Modern C++

24260
来自专栏Golang语言社区

深入分析golang多值返回以及闭包的实现

一、前言 golang有很多新颖的特性,不知道大家的使用的时候,有没想过,这些特性是如何实现的?当然你可能会说,不了解这些特性好像也不影响自己使用golang,...

53660
来自专栏Java帮帮-微信公众号-技术文章全总结

Java企业面试——Java基础

1. Java基础部分 1.1 Java中的方法覆盖(Overwrite)和方法重载(Overloading)是什么意思? 重载Overload表示同一个类中...

28840
来自专栏Java3y

泛型就这么简单

16740
来自专栏向治洪

迭代子模式

概述 概念:在阎宏博士的《JAVA与模式》中关于迭代子模式的定义是这样的:迭代子模式又叫游标(Cursor)模式,是对象的行为模式。迭代子模式可以顺序地访问一个...

22760
来自专栏公众号_薛勤的博客

一文看透Java8新特性:lambda表达式和Stream API

借用引言中的示例,在调用new Thread的含参构造方法时,我们通过匿名内部类的方式实现了Runnable对象,但其实有用的代码只有System.out.pr...

12910
来自专栏张善友的专栏

C# 内部类

        C#中的内部类能够使用外部类定义的类型和静态方法,但是不能直接使用外部类的实例方法,直接看来,外部类对于内部类的作用更像是一个命名空间,在C#中...

24980
来自专栏用户2442861的专栏

java泛型(一)、泛型的基本介绍和使用

http://blog.csdn.net/lonelyroamer/article/details/7864531

14310
来自专栏Kevin-ZhangCG

[ Java学习基础 ] Java异常处理

34060
来自专栏测试开发架构之路

总结了一些指针易出错的常见问题(四)

指针与结构体 简介:我们可以使用C的结构体来表示数据结构元素,比如链表或树的节点,指针是把这些元素联系到一起的纽带。 typedef struct _pers...

30070

扫码关注云+社区

领取腾讯云代金券