在上一部分中,介绍了两个结构型的模式:Bridge和Decorator。这一部分的内容,将会接着上面的讲解,继续我们的设计模式之旅。
这一部分,除了还会介绍一个结构型的Composite模式之外,还会有两个行为模式登场。实际上在前面的内容中,我们已经接触到行为模式了:Observer和Command就是两个典型的行为模式。行为模式更多的注重于算法和对象建间职责的分配,也就是说,它会更多的关注于这个模式系统之类的各对象协作间的语义,以及在对象间进行通讯的流控制。
Composite模式
毫无疑问的,AWT中的Component-Container体系就是一个很好的Composite模式的例子。Container继承于Component,而Container中有可以包含有多个Component,因为Container实际上也是Component,因而Container也可以包含Container。这样通过Component-Container结构的对象组合,形成一个树状的层次结构。这也就是Composite模式所要做的。
Composite模式是为了简化编程而提出的,一般的在编程的时候,如果严格的区分Component和Container的话,有时候会带来许多不便,而且这些往往是没有必要的。比如,我要在一个Container中放置一个Component,我并不需要知道这个Component到底是一个Container,或者就是一个一般的Component,在父级容器中所要做的,只是记录一个Component的引用,在需要的时候调用Component的绘制方法来显示这个Component。当这个Component确实是一个Container的时候,它可以通过Container重载后的绘制方法,完成对这个容器的显示,并把绘制消息传递给到它的子对象去。也就是说,对一个父级容器而言,它并不不关心,其子对象到底是一个Component,还是一个Container。它需要将Component和Container统一对待。
图十一:Composite模式的类图
Composite模式比较简单,实现起来也不复杂,但是有一定的局限性。比如,在处理树的时候,我们往往需要处理三类对象:子树,页节点和非页节点。而在Composite模式中对于子树和非叶节点的区分并不明显,而是把他们合成为一个Composite对象了。而且在GOF给出的Composite的模式中,对于添加,删除子节点等属于Composite对象的的方法,是放在了Component对象中的,这虽然在实现的时候可以区分开来,但容易造成一些概念上的误解。
由上所叙,我们可以提出一个改进了的Composite模式,引入子树对象,从而将子树和非叶节点分开,如下图所示:
图十二:Composite模式的一种变体
虽然将Composite从Component类层次中分离出来,但并没有损害Composite模式的内涵。这样做不一定就会比上面的那个要好,各有不同的应用,不过有时候用这样的方法来处理子树要容易些,概念上也更为清晰。
下面的代码,给出了一个Composite模式简单的Java实现:
public abstract class Component{
public abstract void operation();
public void add(Component component){};
public void remove(Component component){};
}
import java.util.*;
public class Composite extends Component{
String name;
ArrayList children = new ArrayList();
public Composite(String name){
this.name = name;
}
public void add(Component component){
children.add(component);
}
public void remove(Component component){
children.remove(component);
}
public void operation(){
System.out.println(name);
Iterator iterator = children.iterator();
while(iterator.hasNext()){
Component child = (Component)iterator.next();
child.operation();
}
}
}
public class Leaf extends Component{
String name;
public Leaf(String name){
this.name = name;
}
public void operation(){
System.out.println(name);
}
}
Strategy模式
Strategy模式主要用来将算法实现从类中分离出来,并封装在一个单独的类中。更简单的说,对象与其行为(behaviour)这本来紧密联系的两部分被解耦,分别放在了两个不同的类中。这使得对同一个行为,可以方便的在任何时候切换不同的实现算法。而通过对策略的封装,为其提供统一的接口,也可以很容易的引入新的策略。
AWT的LayoutManager,是Strategy模式的一个例子。对于GUI而言,每个组件(Component)在容器中(Container)的排放是需要遵循一定的算法的。通常的方法是使用绝对坐标,就像VB,Delphi之类的工具所作的那样,记录每个组件在容器中的位置。这当然会带来一些问题,比如在窗体缩放的时候,就需要手工编码改变组件的大小和位置,以使得原来的比例得以保存。而在AWT中,引入了布局管理器(LayoutManager)的概念,使得布局的方法大大丰富,编码过程也变得简单。
一个容器,比如Applet,Panel等,仅仅记录其包含的组件,而布局管理器中封装了对容器中组件进行布局的算法,具体地说,就是指明容器中组件的位置和尺寸的大小。通过布局管理器,你只需要确定想放置的组件间的相对位置即可,这一方面简化编码,另一方面也有助于实现软件的平台无关性。
图十三:AWT中的容器和布局管理器的关系
每一个容器均有一个布局管理器,当容器需要布置它的组件时,它调用布局管理器的方法布置容器内的组件。LayoutManager2继承于LayoutManager,提供更为细致的布局功能,它可以让布局管理器为组件加上约束条件已确定组件如何被布置。例如,为了确定组件被摆放在边框内的位置,BorderLayout在它的组件上加上方向指示。
特别的,通过实现LayoutManager或者LayoutManager2接口,可以很容易实现自定义的布局策略。
回到模式的话题上来,如果有几个很相似的类,其区别仅仅是在个别行为上的动作不同,这时候就可以考虑使用Strategy模式。这样,通过策略组合,将原来的多个类精简为一个带有多个策略的类。这很符合OO设计的原则:找到变化的部分,并将其封装起来!Strategy模式同样的为子类继承提供了一个好的替代方案,当使用继承机制的时候,行为的改变是静态的,你指能够改变一次--而策略是动态的,可以在任何时候,切换任何次数。更为重要的是,策略对象可以在不同的环境中被不同的对象所共享。以布局管理器为例,虽然每一个容器只有一个布局管理器,但是一个布局管理器可以为多个容器工作。
图十四:Strategy模式的类图
Strategy模式也有一些缺点,比如,应用程序必须知道所有的策略对象,并从中选者其一。而且在策略对象被使用的时候,它和Context对象之间通常是紧耦合的,Context对象必须为策略对象提供与具体算法相关的数据或者其它的东西,而这些数据的传递可能并不能够风装载抽象地策略类中,因为并不是所有的算法都会需要这些数据的。另外,因为策略对象通常由应用程序所创建,Context对象并不能够控制Strategy的生命期,而在概念上,这个策略应该从属于Context对象,其生命期不应该超出Context的范围对象。
通常的,Strategy很容易和Bridge模式相混淆。确实,他们有着很相近的结构,但是,他们却是为解决不同的问题而设计的。Strategy模式注重于算法的封装,而Bridge模式注重于分离抽象和实现,为一个抽象体系提供不同的实现。
Iterator模式
Iterator模式用来规格化对某一数据结构的遍历接口。
JDK中在Collection Framework中引入了Iterator接口,提供对一个Collection的遍历。每一个Collection类中都定义有从Collection接口中继承而来的iterator()方法,来得到一个Iterator对象,我们称之为遍历器,Iterator接口很简单:
hasNext():用来判断在遍历器中是否还有下一个元素。
next():返回遍历器中的下一个元素。
remove():在被遍历的Collection类中删除最后被返回的那个对象。
我们就以最为常用的Vector为例,看看在Collection Framework中,Iterator模式是如何被实现的。在此之前,我们需要先了解一些Vector和Collection Framework的结构。
Collection接口作为这个Framework的基础,被所有其它的集合类所继承或者实现。对Collection接口,有一个基本的实现是抽象类AbstractCollection,它实现了大部分与具体数据结构无关的操作。比如判断一个对象是否存在于这个集合类中的contains()方法:
public boolean contains(Object o) {
Iterator e = iterator();
if (o==null) {
while (e.hasNext())
if (e.next()==null)
return true;
} else {
while (e.hasNext())
if (o.equals(e.next()))
return true;
}
return false;
}
而这其中调用的iterator()方法是一个抽象方法,有赖于具体的数据结构的实现。但是对于这个containers()方法而言,并不需要知道具体的Iterator实现,而只需要知道它所提供的接口,能够完成某类任务既可,这就是抽象类中抽象方法的作用。其它的在AbstractCollection中实现的非抽象方法,大部分都是依赖于抽象方法iterator()方法所提供的Iterator接口来实现的。这种设计方法是引入抽象类的一个关键所在,值得仔细领悟。
List接口继承Collection接口,提供对列表集合类的抽象;对应的AbstractList类继承AbstractCollection,并实现了List接口,作为List的一个抽象基类。它对其中非抽象方法的实现,也大抵上与AbstractCollection相同,这儿不再赘叙。
而对应于Collection的Iterator,List有其自己的ListIterator,ListIterator继承于Iterator,并添加了一些专用于List遍历的方法:
boolean hasPrevious():判断在列表中当前元素之前是否存在有元素。
Object previous():返回列表中当前元素之前的元素。
int nextIndex():
int previousIndex():
void set(Object o):
void add(Object o):
ListIterator针对List,提供了更为强劲的功能接口。在AbstractList中,实现了具体的iterator()方法和listIterator()方法,我们来看看这两个方法是如何实现的:
public Iterator iterator() {
return new Itr(); //Itr是一个内部类
}
private class Itr implements Iterator {
int cursor = 0;//Iterator的计数器,指示当前调用next()方法时会被返回的元素的位置
int lastRet = -1;//指示刚刚通过next()或者previous()方法被返回的元素的位置,-1
//表示刚刚调用的是remove()方法删除了一个元素。
//modCount是定义在AbstractList中的字段,指示列表被修改的次数。Iterator用//这个值来检查其包装的列表是否被其他方法所非法修改。
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size();
}
public Object next() {
try {
//get方法仍然是一个抽象方法,依赖于具体的子类实现
Object next = get(cursor);
//检查列表是否被不正确的修改
checkForComodification();
lastRet = cursor++;
return next;
} catch(IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet == -1)
throw new IllegalStateException();
checkForComodification();
try {
//同样remove(int)也依赖于具体的子类实现
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch(IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
这儿的设计技巧和上面一样,都是使用抽象方法来实现一个具体的操作。抽象方法作为最后被实现的内容,依赖于具体的子类。抽象类看起来很像是一个介于接口和子类之间的一个东西。
从设计上来讲,有人建议所有的类都应该定义成接口的形式,这当然有其道理,但多少有些极端。当你需要最大的灵活性的时候,应该使用接口,而抽象类却能够提供一些缺省的操作,最大限度的统一子类。抽象类在许多应用框架(Application Framework)中有着很重要的作用。例如,在一个框架中,可以用抽象类来实现一些缺省的服务比如消息处理等等。这些抽象类能够让你很容易并且自然的把自己的应用嵌入到框架中去。而对于依赖于每个应用具体实现的方法,可以通过定义抽象方法来引入到框架中。
其实在老版本的JDK中也有类似的概念,被称为Enumeration。Iterator其实与Enmeration功能上很相似,只是多了删除的功能。用Iterator不过是在名字上变得更为贴切一些。模式的另外一个很重要的功用,就是能够形成一种交流的语言(或者说文化)。有时候,你说Enumeration大家都不明白,说Iterator就都明白了。
小结:
这部分介绍了三个模式:Composite,Strategy和Iterator。Composite是一个结构性的模式,用来协调整体和局部的关系,使之能够被统一的安排在一个树形的结构中,并简化了编程。Strategy模式与Bridge模式在结构上很相似,但是与Bridge不同在于,它是一个行为模式,更侧重于结构的语义以及算法的实现。它使得程序能够在不同的算法之间自由方便的作出选择,并能够在运行时切换到其他的算法,很大程度上增加了程序的灵活性。Iterator模式提供统一的接口操作来实现对一个数据结构的遍历,使得当数据结构的内部算法发生改变时,客户代码不需要任何的变化,只需要改变相应的Iterator实现,就可以无缝的集成在原来的程序中。
分享到:
相关推荐
### 从Java类库看设计模式之Observer模式详解 #### 一、引言 在软件开发过程中,常常遇到需要在不同对象之间建立依赖关系的情况,其中一个对象的状态变化会影响到另一个对象的行为。这种需求可以用设计模式中的...
"从Java类库看设计模式.pdf" 本文档主要介绍了设计模式的概念、特点和在 Java 类库中的应用,作者从组合和观察者两种设计模式入手,分析了它们的组成、特点和使用条件,并对其在 Java 类库中的应用进行了深入分析。...
除了Observer模式,Java类库还体现了其他多种设计模式。如Singleton模式,用于确保一个类只有一个实例,并提供全局访问点,如`java.lang.Runtime`类;Factory模式,用于创建对象的接口,如`java.sql.DriverManager`...
这些只是众多Java类库中的一小部分,实际上还有许多其他框架,如Apache HttpClient用于网络请求,Jackson或Gson用于JSON序列化和反序列化,Log4j或SLF4J用于日志记录等。开发者可以根据项目需求选择合适的类库,以...
同时,这也是一个很好的学习 Java 设计模式和最佳实践的方式,因为许多类库的设计都遵循了 SOLID 原则和面向对象设计的原则。因此,"java2 类库" 对于 Java 学习者来说是一本经典的参考材料,它不仅提供了丰富的代码...
Java类库大全是一个集合了众多Java...通过这个"Java类库大全",开发者可以根据项目需求快速找到合适的类库,提高开发效率,同时也能学习到各种类库的设计理念和最佳实践。这个资源包无疑是对Java开发者的一份宝贵财富。
`java.beans`包则支持JavaBeans组件,便于属性、事件和设计模式的处理。 最后,`javafx`包是JavaFX图形和媒体库,用于构建现代、丰富的跨平台桌面和Web应用程序,支持2D和3D图形、动画、音频和视频处理。 总的来说...
合理利用Java类库能够极大地提高编程效率,使得程序更加简洁易懂。 #### 二、Java类库的结构与组成 Java类库中的类和接口大多被组织成不同的包(package)。每个包通常包含了一组具有相似功能的类和接口,这些包...
其中,**Decorator模式**作为一种常用的结构型设计模式,在Java类库中有着广泛的应用。本文将详细介绍Decorator模式的概念、应用场景及其在Java I/O系统中的具体实现。 #### 二、Decorator模式简介 **Decorator...
12. **设计模式**:如单例、工厂、观察者等,例子将展示如何在Java中实现常见的设计模式,提升代码的复用性和可维护性。 通过研究这些例子,开发者不仅可以巩固Java基础知识,还能学习到如何在实际项目中灵活运用...
例如,它可能包含了一些数据结构、算法或者设计模式的实现,帮助读者更好地理解和应用Java编程思想。 在Java编程中,类库的使用可以极大地提高开发效率。例如,标准库如Java.util和Java.io提供了大量用于处理集合、...
### Java设计模式详解 #### 一、背景与概念 在软件工程领域,设计模式是一种用于解决常见问题的可重用解决方案。《Java设计模式PDF》是一本由James W. Cooper编写的经典书籍,该书详细介绍了Java编程语言中的设计...
**JAVA设计模式** 在软件工程领域,设计模式是一种在特定情境下解决问题的可复用解决方案。它们是经验的总结,代表了在特定上下文中被广泛接受的、经过时间考验的最佳实践。Java设计模式是面向对象编程中的一种重要...
《FTP客户端Java类库ftp4j的深度解析》 FTP(File Transfer Protocol)是一种用于在网络上进行文件传输的协议,广泛应用于互联网上文件的上传和下载。在Java编程中,为了方便开发者实现FTP功能,有许多优秀的类库可...
本资源“java设计模式(绝对高清和完整)”提供了全面且清晰的关于Java设计模式的学习材料,由“设计模式公司荣誉出品”,确保了内容的专业性和质量。 设计模式并不是具体的代码或库,而是描述在某些特定上下文中,...