论文导读:软件设计模式是一种表达、记录和重用软件设计结构和设计经验的新方法,它对反复出现的设计结构的关键特征进行识别、抽象和命名,使重用成功设计和结构更加容易。本文介绍了软件设计模式的特点、描述方式以及在设计中使用模式带来的好处,并就迭代器(Iterator)模式在Java集合框架类(JavaCollectionFramework)中的典型实现为例,说明此模式的实现方法以及利用此模式带来的软件扩充和使用的方便性。Iterator模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明的访问集合内部的数据。
关键词:设计模式,Iterator模式,Java集合框架类
0 引 言
随着计算机网络和通信的快速发展和广泛应用,市场对企业的软件能力提出了近乎苛刻的要求,提高软件能力来化解软件危机已成为软件业的头等大事。发表论文。构件化和重用是200多年来工业化社会发展的成功经验,借鉴工业社会发展的成功经验,确定可行的软件工业化目标显得非常重要。构件化和重用是提高软件能力的必有之路。软件设计模式是一种表达、记录和重用软件设计结构和设计经验的新方法,它对反复出现的设计结构的关键特征进行识别、抽象和命名,使重用成功设计和结构更加容易。软件设计模式已经成为现代软件系统设计的重要研究对象。发表论文。本文介绍了软件设计模式的特点、描述方式以及在设计中使用模式带来的好处,并就迭代器(Iterator)模式在Java集合框架类(Java Collection Framework)中的典型实现为例,说明此模式的实现方法以及利用此模式带来的软件扩充和使用的方便性。
1 设计模式
设计模式的思想最初来源于建筑领域,但其中体现的思想也适用于其它领域,例如面向对象软件设计领域。设计模式关注的是特定设计问题及其解决方案,在每种模式中均描述一个设计问题和一个经过验证的、通用的解决方案,这个解决方案是对反复出现的设计结构进行识别和抽象得到的,它通常由多个对象组成,模式中不仅描述对象的设计,而且描述对象间的通信。
一个设计模式有四个基本要素:
模式名称:一个助记名,它用一两个词来描述模式的问题、解决方案和效果。
问题(problem):描述应该在何时使用该模式。
解决方案(solution):描述设计的组成成分,它们之间的相互关系及各自的职责和协作方式。
效果(consequences) 描述了模式应用的效果及使用模式应权衡的问题。模式效果包括它对系统的灵活性、扩充性或可移植性的影响,显式地列出这些效果对理解和评价这些模式很有帮助。
根据所解决的问题不同,设计模式可分为创建型模式、结构型模式和行为模式三类。
创建型模式都和如何有效地创建类的实例相关,这些模式使程序能够根据特定的情况创建特定的类。通过new来创建实例只能够在程序中生成固定的类。但是在很多情况下,程序需要根据不同的情况生成不同的类的实例,这就需要将实例的生成过程抽象到一个特殊的创建类中,由该类在运行时决定生成哪种类的实例。这样使得程序有更好的灵活性和通用性。
结构型模式处理类和对象的组合,将类和对象组合起来,以构成更加复杂的结构。它又被划分为类模式和对象模式。类模式和对象模式之间的区别在于类模式通过继承关系来提供有效的接口;而对象模式通过对象合成或将对象包含在其它对象中的方式构成更加复杂的结构行为模式,
行为类型的模式主要是那些处理对象之间通讯的模式。它描述类或对象的交互和职责分配,定义对象间的通信和复杂程序中的流控。本文涉及的迭代器(Iterator)模式就是一种行为模式。
2 迭代器模式(Iterator Pattern)
在面向对象的软件设计中,我们会遇到一类集合对象,这类集合对象的内部结构的实现可能差别很大,但是我们需要关心的只有两点:一是集合内部的数据存储结构,二是遍历集合内部的数据。单一职责原则是面向对象设计的一条重要原则,所以我们要尽可能的去分解这些职责,用不同的类去承担不同的职责。Iterator模式就是分离了集合对象的遍历行为,抽象出一个迭代器类来负责,这样既可以做到不暴露集合的内部结构,又可让外部代码透明的访问集合内部的数据。
迭代器使用的意图是提供一种方法顺序访问一个集合对象中的各个元素,而又不需要暴露该对象的内部细节。现在的电视机,使用[后一个]和[前一个]按钮切换频道。当按下[后一个]按钮时,将切换到下一个预置的频道。作为电视机的用户,当我们改变频道时,我们关心的不是具体的频道,而是节目内容。如果对一个频道的节目不感兴趣,那么可以换下一个频道,而不需要知道它是具体是第几频道,图1给出了使用频道迭代器来选台的对象图。
图1使用选频器做例子的Iterator模式对象图
迭代器模式在客户与容器之间加入了迭代器角色。它可以把访问逻辑从不同类型的集合类中抽象出来,从而避免向客户端暴露集合的内部结构。发表论文。客户端自身不维护遍历集合的'指针',所有的内部状态(如当前元素位置,是否有下一个元素)都由Iterator来维护,客户端从不直接和集合类打交道,它总是控制Iterator,向它发送'向前','向后','取当前元素'的命令,就可以间接遍历整个集合。
为了使客户程序从与具体迭代器耦合的困境中脱离出来,避免具体迭代器的更换给客户程序带来的修改,迭代器模式抽象了具体迭代器,使得客户程序更具一般性和重用性。
Iterator模式结构图如图2所示下:
图2 Iterator模式结构图
从结构上可以看出,迭代器模式由以下角色组成:迭代器角色(Iterator),负责定义访问和遍历元素的接口;具体迭代器角色(Concrete Iterator),实现迭代器接口,并要记录遍历中的当前位置;容器角色(Container),负责提供创建具体迭代器角色的接口;具体容器角色(Concrete Container),实现创建具体迭代器角色的接口,它与该容器的结构相关。
迭代器角色的加入,就可以很好的避免容器内部细节的暴露,而且也使得设计符合“单一职责原则”。具体迭代器角色和具体容器角色是耦合在一起的,即遍历算法是与容器的内部细节紧密相关的。
3 Java类库中Iterator模式实现
Java类库是设计模式的经典应用,在此以Java类库中的集合框架类(Java Collection Framework)为例来讨论Iterator模式的实现。
在JavaCollection Framework的应用中,遍历的进程是由客户程序来控制的,这种实现方式被称为外部迭代器;它比由迭代器自身来控制迭代的内部迭代器更加灵活、强大。
遍历算法的实现,表面上看应该在迭代器角色中实现。这样既便于在一个容器上使用不同的遍历算法,也便于将一种遍历算法应用于不同的容器。但是这样就要求容器角色公开自己的私有属性,从而破坏掉容器的封装性。所以遍历算法被放到容器角色里来实现。这样遍历算法便和特定的容器捆绑在一起,迭代器角色仅仅存放一个遍历的当前位置。这样做的另一个好处是对同一个容器对象,可以同时进行多个遍历。因为遍历状态是保存在每一个迭代器对象中的。在Java Collection Framework的应用中,提供的具体迭代器角色是定义在容器角色中的内部类,这样保护了容器的封装。同时容器也提供了遍历算法接口,便于扩展各自的迭代器。
下面给出迭代器模式中的四个角色在JDK5.0中的JavaCollection Framework中所对应的代码片段,以此来分析迭代器模式如何在Java Collection Framework中实现,图3给出了我们要分析的基本类的类图:
图3 Java Collection Framework中一个基本类图
1)迭代器角色,接口Iterator,定义了遍历的接口,
代码片段1
public interfaceIterator<E> {
boolean hasNext();
E next();
void remove();
}
2)容器角色,接口List。
3)具体容器角色,实现List接口并继承抽象类AbstractList的ArrayList类。
4)具体迭代器角色,它是以抽象类AbstractList的内部类的形式出来的。抽象类AbstractList是为了将各个具体容器角色的公共部分提取出来而存在的。
代码片段2所示的方法iterator()是负责创建具体迭代器角色的工厂方法。
代码片段2:
publicIterator<E> iterator() {
return new Itr();
}
作为内部类的具体迭代器角色,其类图如图4所示,具体实现的代码如代码段3所示。
图4 具体迭代器角色类图
代码片段3:
private class Itrimplements Iterator<E> {
intcursor = 0;
intlastRet = -1;
intexpectedModCount = modCount;
publicboolean hasNext() {
return cursor != size();
}
publicE next() {
checkForComodification();
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch(IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
publicvoid remove() {
if (lastRet == -1)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor) cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch(IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
finalvoid checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
迭代器模式的使用如下面的代码段4所示,客户程序要先得到具体容器角色,然后再通过具体容器角色得到具体迭代器角色。这样便可以使用具体迭代器角色来遍历容器。
代码片段4
List<E> list=new ArrayList <E>();
Iterator it = list.iterator();
while(it.hasNext()){
it.next();//do some businesss logic
}
4 总结
迭代器模式在应用中使用非常广泛,它通过抽象出一个负责遍历行为的迭代器类,来达到既不暴露集合的内部结构,又可让外部代码透明的访问集合内部数据的目标。
在实现自己的迭代器时,要操作的容器必须有相应的接口支持,通过在容器内增加相应的内部迭代器类来实现对具体容器的遍历,并且还要有创建具体迭代器角色的工厂方法。当然,不同结构的容器角色,其遍历的含义和实现也有较大差别,特别是当容器中存在复合对象或数据结构复杂时,如何进行深层遍历以及遍历的安全性都是值得关注的问题。
[参 考文 献]
[1](美)GoF。设计模式--可复用的面向对象软件的基础,机械工业出版社,2005.
[2]深入浅出Java设计模式, http://www.cn-java.com.
[3].NET设计模式, http://www.cnblogs.com.
[4]龚波。java设计模式,人民邮电出版社,2007.
[5]饶一梅,王治宝,王秀峰。软件设计模式及其在Java类库中的典型实现。[J]计算机工程与应用,2002/4,P48—50.
|