天天看點

Decorate的三種實作方法

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在擴充性方面相當困難,并且與面向對象的原則會産生沖突。

繼續閱讀