`

Java类库看设计模式Composite,Strategy和Iterator

 
阅读更多

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模式中对于子树和非叶节点的区分并不明显,而是把他们合成为一个Composite对象了。而且在GOF给出的Composite的 模式中,对于添加,删除子节点等属于Composite对象的的方法,是放在了Component对象中的,这虽然在实现的时候可以区分开来,但容易造成 一些概念上的误解。

由上所叙,我们可以提出一个改进了的Composite模式,引入子树对象,从而将子树和非叶节点分开,如下图所示:


图十二: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中的容器和布局管理器的关系
图十三:AWT中的容器和布局管理器的关系

每一个容器均有一个布局管理器,当容器需要布置它的组件时,它调用布局管理器的方法布置容器内的组件。LayoutManager2继承于 LayoutManager,提供更为细致的布局功能,它可以让布局管理器为组件加上约束条件已确定组件如何被布置。例如,为了确定组件被摆放在边框内的 位置,BorderLayout在它的组件上加上方向指示。

特别的,通过实现LayoutManager或者LayoutManager2接口,可以很容易实现自定义的布局策略。

回到模式的话题上来,如果有几个很相似的类,其区别仅仅是在个别行为上的动作不同,这时候就可以考虑使用Strategy模式。这样,通过策略组合,将原 来的多个类精简为一个带有多个策略的类。这很符合OO设计的原则:找到变化的部分,并将其封装起来!Strategy模式同样的为子类继承提供了一个好的 替代方案,当使用继承机制的时候,行为的改变是静态的,你指能够改变一次--而策略是动态的,可以在任何时候,切换任何次数。更为重要的是,策略对象可以 在不同的环境中被不同的对象所共享。以布局管理器为例,虽然每一个容器只有一个布局管理器,但是一个布局管理器可以为多个容器工作。


图十四: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设计模式;java语言描述;经典的设计模式

    Java语言以其跨平台特性和丰富的类库,成为实现设计模式的理想选择。无论你是否熟悉C++或其他编程语言,通过Java来理解和应用设计模式,都能够受益匪浅。Java的面向对象特性,如封装、继承和多态,与设计模式的理念...

    Java设计模式菜鸟系列教程

    Java设计模式菜鸟系列教程 本教程对Java 23种设计模式进行了总结,涵盖了策略模式、观察者...本教程对Java设计模式的详细介绍和应用场景分析,能够帮助读者更好地理解和应用设计模式,提高代码的可维护性和可扩展性。

    设计模式Java版各个实现代码

    在Java中,设计模式的应用能够提高代码的可读性、可维护性和可扩展性,是每个Java开发者必备的技能之一。本资料集“设计模式Java版”包含了各种设计模式的实现代码,旨在帮助学习者深入理解和应用这些模式。 1. **...

    java设计模式的分析及其应用

    在实际的Java类库中,许多设计模式都有所体现,如ArrayList和LinkedList实现了List接口,这就是装饰模式的应用;Hibernate框架中的Session和SessionFactory体现了单例模式;Spring框架中的AOP(面向切面编程)运用了...

    23种设计模式实现designpattern书籍代码的重写Java版

    本资源提供了《设计模式:可复用面向对象软件的基础》(GOF设计模式)书籍中23种设计模式的Java实现重写版本。下面将对这23种设计模式进行详细解释,并结合提供的文件名来推测每种模式的示例应用。 1. **单例模式...

    第14讲谈谈你知道的设计模式1

    在 Java 基础类库中,有很多典型的设计模式应用案例。例如,InputStream 是一个抽象类,标准类库中提供了FileInputStream、ByteArrayInputStream 等各种不同的子类,分别从不同角度对 InputStream 进行了功能扩展,...

    java联盟出品(23种设计模式大全)

    ### Java设计模式精讲:23种模式全解析 #### 创建型模式:构建与实例化对象的艺术 在软件工程中,创建型模式是一类关注于如何高效、灵活地实例化对象的设计模式。这类模式提供了创建对象的最佳实践,使得对象的...

    研磨设计模式PDF

    《研磨设计模式》这本书是陈臣和王斌两位作者合作的成果,专注于讲解软件设计中的模式应用。设计模式是软件工程中的一种最佳实践,它总结了在特定上下文中解决问题的常见方法,使得开发者可以复用这些解决方案,提高...

    Java开发 -- 设计模式

    ### Java开发中的23种设计模式 在软件工程领域,设计模式是一种被广泛采用的解决方案,用于解决在软件设计过程中经常遇到的问题。设计模式能够帮助开发者编写出更加灵活、可扩展且易于维护的代码。Java作为一门流行...

    java中的23种设计模式

    Java中的23种设计模式为开发者提供了一套解决常见问题的工具箱,它们不仅有助于提高代码的可读性和可维护性,还能促进团队间的沟通和代码共享。理解并熟练掌握这些模式,是成为一名高级Java开发人员的必经之路。每种...

    设计模式精解-GoF23种设计模式解析附C实现源码.pdf

    《设计模式精解-GoF23种设计模式解析附C实现源码》这份资料深入浅出地介绍了软件工程领域著名的“Gang of Four”(GoF)所提出的23种设计模式,不仅提供了理论上的解析,还附带了具体的C语言实现源码,为读者提供了...

    JAVA 设计模式讲解

    ### JAVA 设计模式详解 #### 一、设计模式概述 设计模式是一种在特定情况下解决软件设计问题的方法。通过学习和应用这些模式,开发者能够更好地组织和优化代码结构,提高程序的可读性和可维护性。设计模式并不是...

    Java设计模式

    Java设计模式是软件开发中的一种重要概念,它们是经过时间考验和广泛实践的解决方案模板,用来解决常见的设计问题。设计模式并非具体的代码或类库,而是描述在特定上下文中解决问题的通用可重用方案。本文档涵盖了23...

    23种设计模式的实现(Java 版)

    Java作为一种广泛应用的面向对象编程语言,其丰富的类库和强大的面向对象特性使得设计模式在Java开发中尤为重要。本资源包含23种经典设计模式的Java实现,下面将对这些设计模式进行详细阐述。 1. **单例模式...

    设计模式复习笔记大全

    ### 设计模式复习笔记大全 #### 一、设计模式概览 **设计模式**是一种用于在特定场景下解决常见设计问题的方案...以上是设计模式的一些基础理论和实践指导,希望能够帮助你在软件开发过程中更加灵活高效地解决问题。

    GOF-设计模式-Design Patterns-英文原版-高清-有目录-有页码

    本书还附带了一些基础类库的介绍,例如列表(List)、迭代器(Iterator)、列表迭代器(ListIterator)、点(Point)、矩形(Rect)等,这些都是在实现设计模式时常用的数据结构。 #### 十一、参考文献与索引 **参考文献与...

Global site tag (gtag.js) - Google Analytics