疑惑之初:
记得初次编程的时候,看到书上说过当要想实现对某个对象进行迭代器访问的方式时,需要实现一些特定的接口。
比如说我们想用如下的一段代码:
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图则如下:
进一步改进:
实际上,从图中我们可以看到,两个类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.这也是后续文章要继续讨论的话题。
相关推荐
IteratorPattern 迭代设计模式
迭代器模式(Iterator Pattern)是设计模式中的一种行为模式,它允许顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。迭代器模式提供了一种方法,可以顺序地访问一个聚合对象中的各个元素,而又...
提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示。
源代码(eclipse直接打开) 博文链接:https://futrueboy.iteye.com/blog/382010
主要介绍了C#迭代器模式(Iterator Pattern),包括了迭代器的适用范围及用法实例,需要的朋友可以参考下
迭代器模式(Iterator Pattern) 基本介绍 迭代器模式,提供一种遍历集合元素的统一接口,用一致的方法遍历集合元素,不需要知道集合对象的底层表示,即:不暴露其内部的结构。 提供一种可以遍历聚合对象的方式。又...
iterator_jsonDemo1的链接:http://download.csdn.net/detail/cafebar123/8816409 运用了Extjs,Struts2, json,iterator技术, 将数据从后台传到Extjs表现层(自带json相关jar包)。注释详细,供参考。 Use ...
Head First 设计模式 (九) 迭代器与组合模式(Iterator & Composite pattern) C++实现
Iterator模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明的访问集合内部的数据。提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露...
NULL 博文链接:https://davyjones2010.iteye.com/blog/1876389
Boost Iterator_adaptor
该文档是演示迭代器Iterator的使用方法和源代码,其中包括了Iterator的继承类的讲解和再Iterator中的两种方法
图解设计Iterator 源代码
是关于iterator的函数,以及它们的作用!
本资源模拟java ArrayList Iterator的实现方式,其实也是为了介绍一下Iterator的设计模式。
java-util-iterator.pdfjava-util-iterator.pdfjava-util-iterator.pdf
备忘录模式(Memento Pattern) 策略模式(Strategy Pattern) 抽象工厂模式(Abstract Factory Pattern) 代理模式(Proxy Pattern) 单例模式(Singleton Pattern) 迭代器模式(Iterator Pattern) 访问者模式(Visitor ...
14、迭代器模式ITERATOR PATTERN 15、组合模式COMPOSITE PATTERN 16、观察者模式OBSERVER PATTERN 17、责任链模式 18、访问者模式VISITOR PATTERN 19、状态模式 20、原型模式 21、中介者模式 22、解释器模式 23、...
逆序reverse_iterator打印 /* 编写一段程序,用普通迭代器顺序打印一个vector,再用逆序reverse_iterator打印一个它 */