`

Java类库看设计模式Bridge模式和Decorator

 
阅读更多

http://www.ibm.com/developerworks/cn/java/l-jdkdp/part3/#

 

 

上 一次主要介绍了几个创建型的设计模式AbstractFactroy,FactoryMethod和Singliton。它们的共同的特点,都是用来创建 对象的。这次接下来的内容,涉及到的是几个结构型的模式。所谓结构型模式,就是用来解决在创建系统结构的过程中,通过对类或者对象进行合理有效的组合,以 获得更大的结构的方法。这儿主要讲到了Bridge模式和Decorator模式。对于Bridge模式可能需要更多的理解,因为它在很大程度上说,例示 了设计模式的基本的设计思路和原则。

 

Bridge模式

当初Java刚刚推出来的时候,AWT可是一个比较热的话题,虽然现在有被Swing取代的趋势。但是我一直都觉得AWT也有其优势,至少它使用的本地代码就要比Swing快上许多,而且,可以为用户提供熟悉的本地操作系统界面。如果在Windows XP中运行基于AWT的程序的话,XP中绚烂多变的界面Theme可以轻易应用到AWT程序中,而Swing就不行了,因为AWT所调用的是本带代码,使用的是本地的窗体控件。当然,Swing也有其好处,不可一概而论。

简单来讲,AWT提供对程序员的是对窗体界面系统的抽象,而在内部实现中,针对每一种操作系统,分别有不同实现,这就是同位体(Peer)的概念。当程序 员调用AWT对象时,调用被转发到对象所对应的一个Peer上,在由Peer调用本地对象方法,完成对象的显示。例如,如果你使用AWT创建了一个 Menu类的实例,那么在程序运行时会创建一个菜单同位体的实例,而由创建的同位体的来实际执行菜单的现实和管理。不同的系统,有不同的同位体实 现,Solaris JDK将产生一个Motif菜单的同位体,Windows下的JDK将产生一个Windows的菜单的同位体,等等。同位体的使用,使得交叉平台窗口工具 的开发变得极为迅速,因为同位体的使用可以避免重新实现本地窗口控件中已经包含的方法。


图九:AWT中的组件和其对等体
图九:AWT中的组件和其对等体

实际上,从设计的角度来看,这是一个抽象和实现分离的过程--AWT是抽象,同位体是实现,抽象和实现各自成为一个对象体系,它们由一个桥连接起来,可以 各自发展各自的对象层次,而不必顾虑另一方面。这就是Bridge模式所提供的思想。Bridge模式更可以提供在各个不同的实现中动态的进行切换,而不 必从新编译程序。

通常,Bridge模式和AbstractFactory模式一起工作,由AbstractFactory来创建一个具体实现的对象体系。特殊的,当只有 一个实现的时候,可以将Implementor抽象类去掉。这样,在抽象和实现之间建立起了一一对应的关系,但这并不损害Bridge模式的内涵。这被称 为退化了的Bridge模式。

很多时候,Abstraction层次和Implementor层次之间的方法都不是一一对应的,也就是说,在Abstraction和 Implementor之不是简单的的消息转发。通常,我们会将Abstraction作为一个抽象类(而不是接口)来实现。在Implementor层 次中定义底层的,或者称之为原子方法,而在Abstraction层次中定义一些中高层的基于原子方法的抽象方法。这样,就能更为清晰的划分 Abstraction和Implementor,类的结构也更为清晰。


图十:Bridge模式对系统的划分
图十:Bridge模式对系统的划分

下面,我们来看一个Bridge模式的具体应用。考虑这样的一个问题,需要生成一份报告,但是报告的格式并没有确定,可能是HTML文件,也可能是纯 ASCII文本。报告本身也可能分为很多种,财务报表,货物报表,等等问题很简单,用继承也较容易实现,因为相互之间的组合关系并不是很多。但是,我们现 在需要用Bridge的观点来看问题。

在Bridge模式中,使用一个Report类来描叙一个报告的抽象,用一个Reporter类来描叙Report的实现,它的子类有 HTMLReporter和ASCIIReporter,用来分别实现HTML格式和ASCII格式的报告。在Report层次下面,有具体的一个 StockListReport子类,用来表示货物清单报告。

public abstract class Report
{
    Reporter reporter;
    public Report(Reporter reporter) {
        this.reporter = reporter;
}
//抽象类使用桥接对象的方法来实现一个任务
    public void addReportItem(Object item){
        reporter.addLine(item.toString());
    }
    public void addReportItems(List items){
        Iterator iterator = items.iterator();
        while ( iterator.hasNext() )
        {
            reporter.addLine(iterator.next().toString());
        }
    }
    public String report(){
        return reporter.getReport();
    }
}
public class StockListReport extends Report{
    ArrayList stock=new ArrayList();
    public StockListReport(Reporter reporter){
        super(reporter);
    }
    public void addStockItem(StockItem stockItem){
        stock.add(stockItem);
        addReportItem(stockItem);
    }
}
//实现层次的抽象父类定义原子方法,供抽象层次的类调用
public abstract class Reporter{
    String header = "";
    String trailer = "";
    String report = "";
    public abstract void addLine(String line);
    public void setHeader(String header){
        this.header = header;
    }
    public void setTrailer(String trailer){
        this.trailer = trailer;
    }
    public String getReport(){
        return header+report+trailer;
    }
}
public class HTMLReporter extends Reporter{  
    public HTMLReporter(){
        setHeader("<HTML>\n<HEAD></HEAD>\n<BODY>\n");
        setTrailer("</BODY>\n</HTML>");
    }
    public void addLine(String line){
        report += line + "<BR>\n";
    }
}
public class ASCIIReporter extends Reporter{
    public void addLine(String line) {
        report += line + "\n";
    }
}

 

实际上,Bridge模式是一个很强大的模式,可以应用在很多方面。其基本思想:分离抽象和实现,是设计模式的基础之一。正如GOF所提到的:"找到变化 的部分,并将其封装起来";"更多的考虑用对象组合机制,而不是用对象继承机制"。Bridge模式很好的体现了这几点。


Decorator模式

在使用Java中的IO类库的时候,是不是快要被它那些功能相似,却又绝对可称得上庞杂的类搞得要发疯了?或许你很不明白为什么要做这么多功能相似的几十个类出来,这就是Decorator模式将要告诉你的了。

在IO处理中,Java将数据抽象为流(Stream)。在IO库中,最基本的是InputStream和OutputStream两个分别处理输出和输 入的对象(为了叙述简便起见,这儿只涉及字节流,字符流和其完全相似),但是在InputStream和OutputStream中之提供了最简单的流处 理方法,只能读入/写出字符,没有缓冲处理,无法处理文件,等等。它们只是提供了最纯粹的抽象,最简单的功能。

如何来添加功能,以处理更为复杂的事情呢?你可能会想到用继承。不错,继承确实可以解决问题,但是继承也带来更大的问题,它对每一个功能,都需要一个子类 来实现。比如,我先实现了三个子类,分别用来处理文件,缓冲,和读入/写出数据,但是,如果我需要一个既能处理文件,又具有缓冲功能的类呢?这时候又必须 在进行一次继承,重写代码。实际上,仅仅这三种功能的组合,就已经是一个很大的数字,如果再加上其它的功能,组合起来的IO类库,如果只用继承来实现的 话,恐怕你真的是要被它折磨疯了。


图六:JDK中IO流的类层次
图六:JDK中IO流的类层次

Decorator模式可以解决这个问题。Decorator字面的意思是装饰的意思,在原有的基础上,每添加一个装饰,就可以增加一种功能。这就是 Decorator的本意。比如,对于上面的那个问题,只需要三个Decorator类,分别代表文件处理,缓冲和数据读写三个功能,在此基础上所衍生的 功能,都可以通过添加装饰来完成,而不必需要繁杂的子类继承了。更为重要的是,比较继机制承而言,Decorator是动态的,可以在运行时添加或者去除 附加的功能,因而也就具有比继承机制更大的灵活性。

上面就是Decorator的基本思想,下面的是Decorator模式的静态结构图:


图七:Decorator模式的类图
图七:Decorator模式的类图

可以看到,一个Decorator与装饰的Subject对象有相同的接口,并且除了接口中给出的方法外,每个Decorator均有自己添加的方法,来 添加对象功能。每个Decorator均有一个指向Subject对象的引用,附加的功能被添加在这个Subject对象上。而Decorator对象本 身也是一个Subject对象,因而它也能够被其他的Decorator所修饰,提供组合的功能。

在Java IO操作中,经常可以看到诸如如下的语句:

myStringBuffer=new StringBuffer("This is a sample string to be read");
FilterInputStream myStream=new LineNumberInputStream
( new BufferInputStream( new StringBufferInputStream( myStringBuffer)));
myStream.read();
myStream.line();

 

多个的Decorator被层叠在一起,最后得到一个功能强大的流。既能够被缓冲,又能够得到行数,这就是Decorator的威力!

不仅仅如此,Java中的IO还允许你引入自定义的Decorator,来实现自己想要的功能。在良好的设计背景下,这做起并不复杂,只需要4步:

  1. 创建两个分别继承了FilterInputStream和 FilterOutputStream的子类
  2. 重载read()和write()方法来实现自己想要的功能。
  3. 可以定义或者重载其它方法来提供附加功能。
  4. 确定这两个类会被一起使用,因为它们在功能上是对称的。

 

就这样,你就可以无限的扩展IO的功能了。

在了解了IO中的Decorator后,我们再来看一个Decorator模式应用的具体的例子。这个例子原本是出现在GOF书中的,这儿稍作改动,引来示例。

在一个图形用户界面(GUI)中,一个组件有时候需要用到边框或者滚动条,而有时候又不需要,有时候可能两者都要用到。当需要动态的去处或者添加职能的时 候,就可以考虑使用Decorator模式了。这儿对于一个VisualComponent组件对象,我们引入了两个Decorator 类:BoderDecorator和ScrollDecorator,分别用来为组件添加边框和处理滚动。程序类图如下:


图八:Decorator模式的应用例子
图八:Decorator模式的应用例子

程序写得很简单,没有包括具体的代码,只是有一个可以运行的框架以供参考。代码如下:

//Client类用来创建窗体和组件对象,这儿可以看到Decorator是如何组合和应用的
class Client{
   public static void main (String[] args ){
      Window   window   = new Window ();
      TextView textView = new TextView ();
      window.setContents (
     new BorderDecorator (
         new ScrollDecorator (textView, 500), 1));
   } 
}
//Windows类用来容纳组件对象
class Window{
   VisualComponent contents;
   public Window () {} 
   public void setContents (VisualComponent vc){
      contents = vc;
   } 
}
//VisualComponent类定义了组件的接口
class VisualComponent{
   public VisualComponent (){}
   public void draw (){}
   public void resize (){}
}
//TextView类是一个显示文本的具体的组件
class TextView extends VisualComponent{
   public TextView (){}
   public void draw (){
    …
    }
   public void resize (){
    …
    }
}
//Decorator类继承于VisualComponent,定义所有Decorator的缺省方法实现
class Decorator extends VisualComponent{
   private VisualComponent component;
   public Decorator (VisualComponent vc) {
    this.component=vc;
   }
   public void draw () {
      component.draw ();
   }
   public void resize () {
      component.resize ();
   }
}
//BorderDecorator类为组件提供边框
class BorderDecorator extends Decorator{
   private int width;
   public BorderDecorator (VisualComponent vc, int borderWidth){
      super (vc);
      width = borderWidth;
   }
   public void draw (){
      super.draw ();
      drawBorder (width);
      }
  private void drawBorder (int width){
    …
      }
}
//ScrollDecorator类为组件提供滚动条
class ScrollDecorator extends Decorator{
   private int scrollSize;
   public ScrollDecorator (VisualComponent vc, int scrSize){
      super (vc);
      scrollSize = scrSize;
   }
   public void draw (){
      scroll();
      super.draw ();
   }
   private void scroll (){
    …
   }
}

 

Decorator确实能够很好的缓解当功能组合过多时子类继承所能够带来的问题。但是在得到很大的灵活性的同时,Decorator在使用时也表现得较 为复杂。看看仅仅为了得到一个IO流,除了要创建核心的流外,还要为其加上各种各样的装饰类,这使得代码变得复杂而难懂。有几个人一开始时没有被Java 的IO库吓一跳呢?


小结:

Bridge模式用来分离抽象和实现,使得这两个部分能够分别的演化而不必修改另外一部分的内容。通常的,可以在实现部分定义一些基本的原子方法,而在抽 象部分则通过组合定义在实现层次中的原子方法来实现系统的功能。Decorator模式通过聚合机制来为对象动态的添加职责,解决了在子类继承中容易引起 的子类爆炸的问题。

分享到:
评论

相关推荐

    JAVA设计模式

    **JAVA设计模式** 在软件工程领域,设计模式是一种在特定情境下解决问题的可复用解决方案。它们是经验的总结,代表了在特定上下文中被广泛接受的、经过时间考验的最佳实践。Java设计模式是面向对象编程中的一种重要...

    java设计模式;java语言描述;经典的设计模式

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

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

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

    java设计模式解释

    ### Java设计模式详解 在软件开发领域,设计模式是一种被广泛使用的解决常见问题的方法论,它们可以帮助开发者写出更高效、可维护的代码。本文将基于一份由“牛人”整理的设计模式材料,对其进行详细的解读与扩展,...

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

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

    JAVA设计模式.zip

    以上便是Java设计模式的基本介绍,这些模式不仅有助于提升代码质量,还能帮助开发者更好地理解和解决问题,是每个Java程序员必备的知识点。在实际开发中,根据项目需求灵活运用设计模式,可以有效提高软件的可维护性...

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

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

    设计模式java实现代码

    在这个"设计模式java实现代码"的压缩包中,我们可以期待找到各种设计模式的Java代码示例。 1. **单例模式**(Singleton):确保一个类只有一个实例,并提供一个全局访问点。在Java中,通常使用双重检查锁定(Double...

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

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

    研磨设计模式PDF

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

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

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

    设计模式 设计模式 思想 模式 原则

    1994年,Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 四位作者合作出版了一本书《设计模式:可复用面向对象软件的基础》(Design Patterns: Elements of Reusable Object-Oriented Software),这...

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

    下面我们将详细探讨这些设计模式,分为三大类:创建型模式、结构型模式和行为型模式。 ### 创建型模式 #### 1.1 Factory模式 Factory模式是一种用于封装对象实例化过程的模式,通过提供一个创建对象的接口,但允许...

    设计模式(Patterns in Java)

    本资源《设计模式(Patterns in Java)》是一本专门针对Java开发者的关于设计模式的著作。 1. **单例模式(Singleton)**:确保一个类只有一个实例,并提供全局访问点。在Java中,可以通过双重检查锁定、静态内部类或者...

    疯狂Java Java的23中设计模式

    在探讨《疯狂Java:Java的23种设计模式》这一主题时,我们深入解析了设计模式在软件工程领域的核心作用,以及如何通过这些模式优化代码结构、提高代码复用性和可维护性。以下是对该书提及的23种设计模式的详细解读:...

    Java开发 -- 设计模式

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

Global site tag (gtag.js) - Google Analytics