程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 從Java類庫看設計模式(3)

從Java類庫看設計模式(3)

編輯:關於JAVA

上一次主要介紹了幾個創建型的設計模式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是抽象,同位體是實 現,抽象和實現各自成為一個對象體系,它們由一個橋連接起來,可以各自發展各自的對象 層次,而不必顧慮另一方面。這就是Bridge模式所提供的思想。Bridge模式更可以提供在各 個不同的實現中動態的進行切換,而不必從新編譯程序。

通常,Bridge模式和AbstractFactory模式一起工作,由AbstractFactory來創建一個具體 實現的對象體系。特殊的,當只有一個實現的時候,可以將Implementor抽象類去掉。這樣, 在抽象和實現之間建立起了一一對應的關系,但這並不損害Bridge模式的內涵。這被稱為退 化了的Bridge模式。

很多時候,Abstraction層次和Implementor層次之間的方法都不是一一對應的,也就是說 ,在Abstraction和Implementor之不是簡單的的消息轉發。通常,我們會將Abstraction作為 一個抽象類(而不是接口)來實現。在Implementor層次中定義底層的,或者稱之為原子方法 ,而在Abstraction層次中定義一些中高層的基於原子方法的抽象方法。這樣,就能更為清晰 的劃分Abstraction和Implementor,類的結構也更為清晰。

圖十: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流的類層次

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步:

創建兩個分別繼承了FilterInputStream和 FilterOutputStream的子類

重載read()和write()方法來實現自己想要的功能。

可以定義或者重載其它方法來提供附加功能。

確定這兩個類會被一起使用,因為它們在功能上是對稱的。

就這樣,你就可以無限的擴展IO的功能了。

在了解了IO中的Decorator後,我們再來看一個Decorator模式應用的具體的例子。這個例 子原本是出現在GOF書中的,這兒稍作改動,引來示例。

在一個圖形用戶界面(GUI)中,一個組件有時候需要用到邊框或者滾動條,而有時候又 不需要,有時候可能兩者都要用到。當需要動態的去處或者添加職能的時候,就可以考慮使 用Decorator模式了。這兒對於一個VisualComponent組件對象,我們引入了兩個Decorator類 :BoderDecorator和ScrollDecorator,分別用來為組件添加邊框和處理滾動。程序類圖如下 :

圖八: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模式通過聚合機制來為對象動態的添 加職責,解決了在子類繼承中容易引起的子類爆炸的問題。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved