程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA編程入門知識 >> Java中Decorate的三種實現方法

Java中Decorate的三種實現方法

編輯:JAVA編程入門知識

  每一位讀過GoF的那本聞名的設計模式一書的人都會知道Decorator模式。現在,讓我們暫時忘記所了解的Decorator概念,嘗試著從我們的開發經驗中去理解Decorator模式吧。
  
  Decorator是用於裝飾一個事物(或人)的另一個事物(或人)。一個Decorator直接改變被裝飾對象的職責或特征,但是不能改變被裝飾對象的自有屬性。例如:一個鏡框可以裝飾圖片,化妝品可以裝飾女孩的臉等等。
  
  從我們的專業角度來討論一些存在的實例:
  
  1 JScrollPane可以裝飾JComponent的視圖部分。JComponent本身並不會被改變,但是增加了一個新的屬性(可滾動)。
  
  2 BufferedInputStream是InputStream的裝飾子,本身BufferedInputStream就是一個InputStream,但是它更快,因為提供了對數據的緩存。
  
  3 考慮一下DebugButton,它與JButton一樣,但是它在被點擊時可以向日志文件添加消息。DebugButton是JButton的裝飾子,因為它直接改變了JButton但並沒有改變它的自有屬性。
  
  4 再又如ScrollOverButton,它增加了一個鼠標滑過的行為。當鼠標移出時它是平的,當鼠標經過時它具有一個凸起的邊框。很顯然,ScrollOverButton也是JButton的裝飾子。
  
  現在,我們知道Decorator可能有三種不同的實現:
  
  1 繼續(Inheritance)
  2 封裝(Wrapper)
  3 外掛(External)
  
  本文將討論每一個實現模型,以及它們的優缺點。
  
  繼續
  
  對於開發人員而言,最直觀的Decorator實現就是:寫一個派生類,它繼續自被裝飾類,並賦於新的職責。新的職責可以是通過增加方法或是修改已有方法來實現。
  
   public class DebugButton extends JButton
  {
  public DebugButton()
  {
  addActionListener(new ActionListener()
  {
  System.out.println("debug message");
  });
  }
  }
  此外,我們也可以用相同的方式來實現ScrollOverButton:不是增加ActionListener,而是增加MouseListener。在MouseListener回調方法中改變JButton的邊框,當mouseEntered()被調用時,將邊框從EmpetyBorder變為RaisedBevelBorder。而當mouseExited()方法被調用時,再將邊框從RaisedBevelBorder恢復成EmpetyBorder。
  
  對於BufferedInputStream,同樣實現也是非常簡單的。修改每個讀數據的方法,讓它從內存緩沖區來讀取數據。假如緩沖區是空的,它可以通過super.read()方法來獲取數據並填充緩沖區。JScrollPane,要實現起來就有點復雜,下面我將討論為什麼它會比較難以用繼續的方式來實現。
  
  討論一下繼續方式實現Decorator模式的優點與缺點:
  
    優點
  
  1 我們幾乎可以用這個方式實現所有的Decorator。
  
  2 使用繼續方式實現Decorator模式,可以保留被裝飾類的原始類型,這一點是非常重要的。用繼續方式,我們仍可以使用被裝飾類的在被裝飾之前的類型,例如,我們可以在我們的應用程序中使用crollOverButton代替JButton,但是JScrollPane就不能代替包含在它內部的對象。
  
    缺點
  
  1 用繼續的方式仍不夠直接。設想一下我們實現了ScrollOverButton和DebugButton,但是我們又需要實現一個既有ScrollOverButton特點又有DebugButton行為的按鈕。怎麼辦?用繼續方式我們唯一的選擇就是再派生出一個ScrollOverDebugButton類。假如我們有了ScrollOverDebugButton的實現,那麼是否還需要繼續保留ScrollOverButton或DebugButton實現?因為我們可以為ScrollOverDebugButton增加兩對方法來打開或關閉debug或scroll-over的行為:
  
   public void setDebug(boolean b);
  public boolean isDebug();
  
  public void setScrollOver(boolean b);
  public boolean isScrollOver();
  再進一步考慮,假如將來我們有更多的裝飾功能,增加新的U1,U2,......Un個行為。我們是不是要寫一個類,叫U1U2...UnButton?它是不是要包括2n個這樣的方法:
  
   public void setU(boolean b);
  public boolean getU;();
  每增加一個新的行為(Un+1)給裝飾器就需要增加兩個新的方法,並要修改這個裝飾器的代碼實現。這明顯與面向對象的思想相悖,可能會產生嚴重的後果。(注重:Javax.swing.JButton就是這樣實現的)。
  
  2 多數可視化對象的行為是由風格參數來指定的,而風格的改變是不可預知的。當風格發生了改變,我們不得不調整自己的改變。正如上面所述,使用繼續的方式可能需要改變實現的代碼。
  
  3 要保證被裝飾類的原始類型也不是一件輕易的事。我們需要重載每個構造子,有時甚至是靜態方式。盡管這不困難,但總是相當麻煩的一件事。
  
  用繼續方式來實現Decorator模式並不象我們先前想像的那麼簡單。許多時候,我們並不知道將來我們需要哪一些裝飾器,結果是,使用繼續方式的Decorator在擴展性方面相當困難,並且與面向對象的原則會產生沖突。
  封裝(Wrapper)
  
  封裝實現最主要的思想是將被裝飾的對象封裝入Decorator模式中。Decorator將外界請求轉發給封裝的被裝飾對象,並且在轉發之前(或之後)執行自己新增的功能,或者也可以提供新的獨立方法來實現新增功能。
  
  讓我們回到剛才的例子並且重新把它們用封裝方式來實現:
  
  1 BufferedInputStream是一個InputStream的封裝,(關於這一點可以參考JDK中java.io.BufferedInputStream類的說明或源碼)。盡管事實上BufferedInputStream也是InputStream的一個派生類。作為封裝,在BufferedInputStream的構造子中獲取了另一個InputStream對象,並且將它作為實例變量保存起來,然後它可以轉發請求到這個內置的InputStream對象中。我們可以使用BufferedInputStream在我們原來使用InputStream場合。
  
  2 JScrollPane也是一個封裝的實現。它轉發請求到被封裝的組件中(它們被稱之為視圖)。要注重的是,我們不能夠使用JScrollPane代替它內部的組件,因為它不支持所有的視圖功能。例如,在JScrollPane的getFont()返回的是JScrollPane的字體而不是視圖的字體。
  
  3 我們可以用這種方式實現DebugButton:

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