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

从Iterator Pattern说起

 
阅读更多

疑惑之初:

记得初次编程的时候,看到书上说过当要想实现对某个对象进行迭代器访问的方式时,需要实现一些特定的接口。

比如说我们想用如下的一段代码:

 

for(ListItem item : itemSet)
    item.xxx();

 那么我们就需要实现java.lang.Iterable<T>接口。这个接口包含的方法细节如下:

 

public interface Iterable<T>
{
        Iterator<T> iterator();
}

同时,我们还需要实现接口Iterator<T> 。

Iterator<T>接口的定义如下:

 

public interface Iterator<E> {
    boolean hasNext();
    E next();
    void remove();
}

 

这么粗粗看来,好像是一种什么很奇怪的手法,但是用了这种手法之后,我们就能用迭代的方式来遍历这个对象元素了。那个这个手法是怎么来的呢?它后面的设计思想是什么呢?我们可以从头开始慢慢推导一下。

 

推导:

假设我们有一个用户信息的类,定义如下:

 

public class UserInfo {
    String firstname;
    String  lastname;
    String address;
    public UserInfo(String firstname, String lastname, String address) {
        this.firstname = firstname;
        this.lastname = lastname;
        this.address = address;
    }
    
    public String getFirstname() {
        return firstname;
    }

    public String getLastname() {
        return lastname;
    }

    public String getAddress() {
        return address;
    }
}

这是一个最简单的类定义。为了保存和处理UserInfo,假设有两个用户用两种不同的方法来存储了一系列的用户信息。一个使用了ArrayList,一个使用了UserInfo[]。

两个类的分别定义如下:

UserInfoList:

 

public class UserInfoList
{
    ArrayList<UserInfo> userList;

    public UserInfoList()
    {
        userList = new ArrayList<UserInfo>();

        addUser("a", "a", "aa");
        addUser("b", "b", "bb");
    }

    public ArrayList<UserInfo> getUserList()
    {
        return userList;
    }
}

UserInfoArray:

 

public class UserInfoArray
{
    static final int MAX_ITEMs = 6;
    int numberOfItems = 0;
    UserInfo[] userArray;

    public UserInfoArray()
    {
        userArray = new UserInfo[MAX_ITEM];
    }

    public void addUser(String firstname, String lastname, String address)
    {
        UserInfo user = new UserInfo(firstname, lastname, address);
        if(numberOfItem >= MAX_ITEMS)
        {
            System.err.println("Can't add user");
        }
        else
        {
            userArray[numberOfItems] = user;
            numberOfItems++;
        }
    }

    public UserInfo[] getUserArray()
    {
        return userArray;
    }
}

 

现在,假设我们要遍历两个包含UserInfo集合的类,我们可能需要写如下的代码:

 

UserInfoList list = new UserInfoList();
ArrayList<UserInfo> userList = list.getUserList();
// traverse
for(int i = 0; i < userList.size(); i++)
{
    UserInfo user = userList.get(i);
    // xxx
}


UserInfoArray array = new UserInfoArray();
UserInfo[] = array.getUserArray();
// traverse
for(int i = 0; i < array.length; i++)
{
    UserInfo user = array[i];
    // xxx
}

前面两个类的定义不同,为了遍历其中的元素,我们执行的细节也不同。这样可能会存在一个问题。如果后面我们还有通过链表实现的元素集合类呢?如果要遍历多个的话,岂不是每个都要不同来处理?

 问题:

仔细看上面的问题,我们针对每个保存集合元素的类,其本身的存储方式是不同的。如果直接用for循环遍历的话,其中必然牵涉到里面的保存实现细节。所以说,问题的根源就是为了能够遍历不同集合元素类,我们把其中的细节给暴露出来了,这样就使得我们每次要遍历的时候不得不面对具体的细节,没法用一个统一的方式来遍历所有类。那么,有没有可能我们用一种统一的方式来遍历他们而不用每个都去具体的处理呢?

 

第一步抽象:

我们前面已经意识到了,问题的根源就是在于遍历的时候细节暴露出来了。那么,如果我们定义一个统一的所谓遍历形式,将他们不同的地方都封装起来,是不是就可以了呢?

比如说,用如下的一种方法来遍历集合元素的话,可以用这种写法:

 

Iterator iterator = userInfoList.createIterator();
while(iterator.hasNext())
{
    UserInfo user = iterator.next();
}

 

 采用这种抽象的一个好处就是,我们只需要提供一个封装了hasNext()和next()方法的对象。hasNext()判断是否有下一个元素,以及用next()方法返回当前元素并移动到下一个元素。具体这个对象是怎么来判断下一个以及移动的我们都不需要管。这就是对变化进行封装的好处。既然我们将变化的部分进行封装了,不同变化在iterator肯定都要有一个实现,而且具体的实现才是针对特定问题自身的。

那么,针对封装了变化这一部分所对应的uml图如下:

封装变化

UserInfoArrayIterator的具体实现如下:

 

public class UserInfoArrayIterator implements Iterator<UserInfo>
{
    UserInfo[] users;
    int position = 0;
    public UserInfoArrayIterator(UserInfo[] users)
    {
        this.users = users;
    }

    public UserInfo next()
    {
        UserInfo user = users[position];
        position++;
        return user;
    }

    public boolean hasNext()
    {
        if(position >= users.length || users[position] == null)
        {
            return false;
        }
        else
            return true;
    }
}

 

 

整体的uml图则如下:

整体uml

进一步改进:

实际上,从图中我们可以看到,两个类UserInfoList和UserInfoArray都有一个方法createIterator()。它主要的目的就是用来返回自己特定的Iterator。如果我们将其进一步抽象的话。可以提取一个专门的接口,它主要就是返回一个Iterator类型的数据。这一步,又和我们面向对象设计原则中面向接口和抽象这一条暗合的。这么一步改进之后,对应的uml图则如下:

改进后

 

 

再回首:

再回到开头提到的部分,我们要实现遍历一个对象的话,需要实现Iterable和Iterator两个接口。实际上实现Iterable接口是为了返回一个实际遍历元素的具体Iterator实现。而实现Iterator接口则是对具体类的遍历细节实现。

结论:

总的来说,如果我们用一句话来概括Iterator Pattern的话,就是提供不同集合元素类的通用遍历方法。

1:为了能够采用通用的形式,我们将变化的部分封装成一个Iterator接口,通过实现这个接口来实现具体的遍历细节。对于使用迭代器的代码来说,就不需要关注遍历的细节了。

2: 为了更好的面向抽象而不是具体类,我们将返回一个具体Iterator的方法也抽象成了一个接口Iterable。

这两点就是Iterator Pattern的核心要旨。

参考材料:

《Head first design patterns》 Iterator Pattern章节部分。

http://docs.oracle.com/javase/7/docs/api/java/util/Iterator.html

 

 

后续部分:

Iterator Pattern在Java中间的实现和使用虽然带来了一定的便利,不过因为要定义一堆的接口和实现,看起来稍显麻烦。于是在别的语言里通过语法糖或者内部增强的方式极大的简化了Iterator Pattern定义和实现。比如说在c#语言中,采用yield return的语法糖,编译器把一些细节帮你给做了。而典型的动态语言如python,则有一套iterator的定义机制。进而演化出来了iterator和generator.这也是后续文章要继续讨论的话题。

  • 大小: 108.1 KB
  • 大小: 93.2 KB
  • 大小: 86.9 KB
分享到:
评论
1 楼 kensunhu 2012-06-12  
Great explanation for iterator pattern,thanks!

相关推荐

    IteratorPattern 迭代设计模式

    迭代器模式(IteratorPattern)是设计模式中的一种行为模式,它提供了一种顺序访问聚合对象元素的方法,同时又不暴露其底层表示。这种模式允许我们遍历集合对象的元素,而无需暴露其内部结构。在Java、C#等面向对象...

    迭代器模式(Iterator Pattern)原理图

    迭代器模式(Iterator Pattern)是设计模式中的一种行为模式,它允许顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。迭代器模式提供了一种方法,可以顺序地访问一个聚合对象中的各个元素,而又...

    Iterator Pattern(迭代模式)

    "Iterator Pattern--Biscuit.png"可能是对迭代模式的可视化解释,通过图片可以更直观地看到各个角色及其交互。其他文件如`.classpath`、`.project`、`build.xml`、`Workspace.sws`等是Eclipse项目配置文件,它们与...

    设计模式(C#)之迭代器模式(Iterator Pattern)

    - **抽象迭代器(Iterator)**:定义了遍历元素的接口,包括初始化、判断是否还有下一个元素、获取当前元素和移动到下一个元素等方法。 - **具体迭代器(ConcreteIterator)**:实现了抽象迭代器接口,具体实现如何...

    DesignPattern : Iterator

    迭代器模式(Iterator Pattern)是设计模式中行为模式的一种,主要解决访问聚合对象内容的问题,使得我们可以在不暴露其内部结构的情况下,顺序访问一个聚合对象的各个元素,而又不破坏其内部结构。这个模式在Java、...

    设计模式(十七)——迭代器模式(Iterator Pattern)

    迭代器模式(Iterator Pattern) 基本介绍 迭代器模式,提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层表示,即:不暴露其内部的结构。 提供一种可以遍历聚合对象的方式。又...

    iterator_adaptor.pdf

    一个简单的例子可能是创建一个只读迭代器,它从基础迭代器中获取值但不允许修改。通过重写`increment`和`dereference`函数,我们可以确保迭代器只能前进且不能修改值: ```cpp struct const_iterator : boost::...

    一个运用Extjs,Struts2, json,iterator技术构建的iterator_jsonDemo2。 将数据从后台传到Extjs表现层。

    iterator_jsonDemo1的链接:http://download.csdn.net/detail/cafebar123/8816409 运用了Extjs,Struts2, json,iterator技术, 将数据从后台传到Extjs表现层(自带json相关jar包)。注释详细,供参考。 Use ...

    Java源码分析:深入探讨Iterator模式

    为了遍历这些容器中的元素,Java引入了迭代器模式(Iterator Pattern),这是一种常用的设计模式,它提供了一种访问聚合对象的方法,而无需暴露其内部表示。本文将详细解析Iterator模式在Java中的实现原理,并通过...

    iterator嵌套,struts2

    在Struts2框架中,`iterator`标签是一个非常重要的组件,用于遍历各种集合对象,如List、Map等。在上述描述中,开发者遇到了一个关于`iterator`标签嵌套使用的问题,涉及到`LinkedHashMap`存储的数据结构。让我们...

    模拟java ArrayList Iterator

    此外,Iterator还提供了remove()方法,用于从原始集合中移除当前元素。 模拟ArrayList的Iterator实现,我们需要创建一个内部类,实现Iterator接口。这个内部类需要维护一个指向ArrayList实际元素的指针,以便于在...

    JAVA中的Iterator的用法

    要使用`Iterator`,首先需要从集合对象获取一个`Iterator`实例。这通常是通过调用集合对象的`iterator()`方法实现的。例如: ```java List&lt;String&gt; list = new ArrayList(); list.add("Hello"); list.add("World");...

    Iterator用法

    ### C++中的Iterator模式详解 #### 什么是Iterator模式? 在软件工程中,设计模式是一种用于解决特定问题的通用可重用解决方案。Iterator模式是其中的一种,其主要目标是在不暴露集合内部结构的情况下,为遍历集合...

    Iterator迭代器讲解

    ### Iterator迭代器详解 #### 一、Iterator简介与概念 在Java编程语言中,`Iterator`接口是一个重要的组件,...无论是从设计模式的角度还是实际应用的角度来看,掌握`Iterator`的工作原理和使用方法都是非常重要的。

    .NET设计模式(18):迭代器模式(IteratorPattern)

    Iterator模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明的访问集合内部的数据。提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露...

    图解设计Iterator 源代码

    首先,让我们从`Iterator`接口开始。`Iterator`接口位于`java.util`包中,其主要方法包括: 1. `boolean hasNext()`: 检查集合中是否还有更多元素。如果存在下一个元素,则返回`true`;否则返回`false`。 2. `E ...

    Head First 设计模式 (九) 迭代器与组合模式(Iterator & Composite pattern) C++实现

    迭代器模式(Iterator Pattern)和组合模式(Composite Pattern)是设计模式中的两种重要结构型模式,它们在软件设计中有着广泛的应用。这两种模式都属于GoF(Gang of Four)设计模式,旨在解决特定的问题,提升代码...

    java-util-iterator.pdf java-util-iterator.pdf

    根据提供的文件信息,本文将深入探讨Java中的`java.util.Iterator`接口及其在集合类中的应用。我们将从以下几个方面进行详细解析: ### 一、集合类的根接口:Collection `Collection`接口是Java集合框架的基础,它...

    struts2 标签iterator使用

    在Struts2框架中,`&lt;s:iterator&gt;`标签是一个非常强大的工具,用于遍历集合或数组中的元素,尤其在处理列表数据时极为有用。通过本文档提供的代码示例,我们将深入探讨`&lt;s:iterator&gt;`标签的使用方法及其与不同数据...

    Iterator迭代器的用法

    该文档是演示迭代器Iterator的使用方法和源代码,其中包括了Iterator的继承类的讲解和再Iterator中的两种方法

Global site tag (gtag.js) - Google Analytics