標籤:
這篇將會介紹裝飾者模式(Decorator Pattern),裝飾者模式也稱為封裝模式(Wrapper Pattern),結構型模式之一,其使用一種對用戶端透明的方式來動態擴充項物件的功能,同時它也是繼承關係的一種替代方案之一,但比繼承更加靈活。在現實生活中也可以看到很多裝飾者模式的例子,或者可以大膽的說裝飾者模式無處不在,就拿一件東西來說,可以給它披上無數層不一樣的外殼,但是這件東西還是這件東西,外殼不過是用來擴充這個東西的功能而已,這就是裝飾者模式,裝飾者的這個角色也許各不相同但是被裝飾的對象本質是不變的。
我們的目標是允許類統一擴充,在不修改現有代碼的情況下,就可搭配新的行為。如能實現這樣的目標,有什麼好處呢?這樣的設計具有彈性,可以應對改變,可以接受新的功能來應對改變的需求,也就是 OO 原則中的對擴充開放和對修改關閉的開閉原則。
轉載請註明出處:http://blog.csdn.net/self_study/article/details/51591709。
PS:對技術感興趣的同鞋加群544645972一起交流
設計模式總目錄
java/android 設計模式學習筆記目錄
特點
動態地給一個對象添加一些額外的職責,就增加功能來說,裝飾者模式相比產生子類更加靈活,提供了有別於繼承的另一種選擇。
裝飾者模式可以靜態,或者根據需要可以動態在運行時為一個對象擴充功能。被裝飾者和眾多的裝飾者都是繼承自一個介面,他們有著一樣的行為特性。裝飾者模式是繼承的另一種選擇方式,繼承是在編譯的時候為類添加新的行為,並且這個改變會影響所有原來該類的實體,裝飾者模式就不一樣,它提供一種能夠在運行時根據需要選擇不同運行對象的功能。裝飾者模式和繼承這兩種方式的不同之處在某些擴充功能的情況下顯得尤為重要,在一些物件導向編程的語言中,類無法在運行時被建立,而且當需要擴張功能時,這些行為往往無法預測,這就意味著在每個可能的情況下,這個類都需要被建立,所以對比之下,裝飾者模式優點在於每個裝飾者都是對象,在運行時被建立,並且能夠在每次使用時根據需要自己組合。
UML類圖
我們現在來看看裝飾者模式的 uml 類圖:
裝飾者模式共有四大角色:
- Component:抽象組件
可以是一個介面或者是抽象類別,其充當的就是被裝飾的原始對象,用來定義裝飾者和被裝飾者的基本行為。
- ConcreteComponent:組件具體實作類別
該類是 Component 類的基本實現,也是我們裝飾的具體對象。
- Decorator:抽象裝飾者
裝飾組件對象,其內部一定要有一個指向組件對象的引用。在大多數情況下,該類為抽象類別,需要根據不同的裝飾邏輯實現不同的具體子類。當然,如果是裝飾邏輯單一,只有一個的情況下我們可以忽略該類直接作為具體的裝飾者。
- ConcreteDecoratorA 和 ConcreteDecoratorB:裝飾者具體實作類別
對抽象裝飾者的具體實現。
在已有的 Component 和 ConcreteComponent 體系下,實現裝飾者模式來擴充原有系統的功能,可以分為 5 個步驟
- 繼承或者實現 Component 組件,產生一個 Decorator 裝飾者抽象類別;
- 在產生的這個 Decorator 裝飾者類中,增加一個 Component 的私人成員對象;
- 將 ConcreteComponent 或者其他需要被裝飾的對象傳入 Decorator 類中並賦值給上一步的 Component 對象;
- 在裝飾者 Decorator 類中,將所有的操作都替換成該 Component 對象的對應操作;
- 在 ConcreteDecorator 類中,根據需要對應覆蓋需要重寫的方法。
裝飾者模式在源碼中用的也是非常多的,在 Java 和 Android 中都能夠見到裝飾者模式的影子:
Java 中的裝飾者模式
最典型的就是 Java 中的 java.io 包下面的 InputStream 和 OutputStream 相關類了,初學 Java 的時候,看到這些類,頭都大了,其實學了裝飾者模式之後,再理解這些類就很簡單了,畫一個簡單的類圖來表示:
InputStream 類是一個抽象組件, FileInputStream,StringBufferInputStream 和 ByteArrayInputStream 類都是可以被裝飾者包起來的具體組件;FilterInputStream 是一個抽象裝飾者,所以它的四個子類都是一個個裝飾者了。
Android 中的裝飾者模式
其次,對於 android 開發工程師來說,最最重要的就應該是“上帝類” Context 和其子類了,這些類我就不用解釋了,上一張類圖基本就明確了:
所以對於 Application,Activity 和 Service 等類來說,他們只是一個個裝飾者,都是用來裝飾 ContextImpl 這個被裝飾者類,Application 是在 createBaseContextForActivity 方法中,通過 ContextImpl 的靜態方法 createActivityContext 獲得一個 ContextImpl 的執行個體對象,並通過 setOuterContext 方法將兩者建立關聯;Activity 是通過 handleLaunchActivity 方法設定的 ContextImpl 執行個體,這個方法會擷取到一個Activity對象,在performLaunchActivity函數中會調用該activity的attach方法,這個方法把一個ContextImpl對象attach到了Activity中,具體可以看看我的這篇部落格,裡面詳細介紹到了 Activity 的啟動過程:android 不能在子線程中更新ui的討論和分析。別的類在這裡就不介紹了,具體的大家可以去網上查閱相關資料。
樣本與源碼
我們這裡以一個圖形系統中的 Window 為例,一般情況視窗都是能夠垂直或者是左右歡動的,所以為了能夠更好的支援 Window 的滑動,給滑動的 Window 加上一個 ScrollBar 是一個不錯的方法,為了重用代碼,水平滑動的 Window 和垂直滑動的 Window 我們就能夠使用裝飾者模式去處理,基本類圖如下所示:
根據類圖,我們首先實現 Window 這個介面:
IWindow.class
public interface IWindow { void draw(); String getDescription();}
然後是被裝飾者 SimpleWindow 類,它實現了視窗的基本行為:
SimpleWindow.class
public class SimpleWindow implements IWindow { @Override public void draw() { Log.e("shawn", "drawing a window"); } @Override public String getDescription() { return "a window"; }}
然後是裝飾者類角色的抽象父類:
WindowDecorator.class
public abstract class WindowDecorator implements IWindow{ private IWindow window; public WindowDecorator(IWindow window) { this.window = window; } @Override public void draw() { window.draw(); } @Override public String getDescription() { return window.getDescription(); }}
最後是實現該裝飾者父類的裝飾者子類:
HorizontalScrollBarDecorator.class
public class HorizontalScrollBarDecorator extends WindowDecorator { public HorizontalScrollBarDecorator(IWindow window) { super(window); } @Override public void draw() { super.draw(); Log.e("shawn", "then drawing the horizontal scroll bar"); } @Override public String getDescription() { return super.getDescription() + " with horizontal scroll bar"; }}
VerticalScrollBarDecorator.class
public class VerticalScrollBarDecorator extends WindowDecorator { public VerticalScrollBarDecorator(IWindow window) { super(window); } @Override public void draw() { super.draw(); Log.e("shawn", "then drawing the vertical scroll bar"); } @Override public String getDescription() { return super.getDescription() + " with vertical scroll bar"; }}
最後測試代碼:
switch (v.getId()) { case R.id.btn_horizontal_window: IWindow horizontalWindow = new HorizontalScrollBarDecorator(new SimpleWindow()); horizontalWindow.draw(); Log.e("shawn", "window description : " + horizontalWindow.getDescription()); break; case R.id.btn_vertical_window: IWindow verticalWindow = new VerticalScrollBarDecorator(new SimpleWindow()); verticalWindow.draw(); Log.e("shawn", "window description : " + verticalWindow.getDescription()); break;}
結果:
com.android.decoratorpattern E/shawn: drawing a windowcom.android.decoratorpattern E/shawn: then drawing the horizontal scroll barcom.android.decoratorpattern E/shawn: window description : a window with horizontal scroll barcom.android.decoratorpattern E/shawn: drawing a windowcom.android.decoratorpattern E/shawn: then drawing the vertical scroll barcom.android.decoratorpattern E/shawn: window description : a window with vertical scroll bar
代碼一目瞭然,結構清晰。
其實說到底,每一個寫過 Android 程式的人都應該用過裝飾者模式,因為每寫一個 Activity,就相當於是寫了一個裝飾者類,不經意間就用了裝飾者模式,大家想一想是不是,哈哈~~
總結
裝飾者模式和代理模式有點類似,很多時候需要仔細辨別,容易混淆,倒不是說會把代理模式看成裝飾者模式,而是會把裝飾者模式看作代理模式。區分一下,裝飾者模式的目的是透明地為用戶端對象擴充功能,是繼承關係的一種替代方案,而代理模式則是給一個對象提供一個代理對象,並由代理對象來控制對原有對象的引用。裝飾者模式應該為所裝飾的對象增強功能;代理模式對代理的對象施加控制,但不對對象本身的功能進行增強。
同時有幾個要點需要提一下:
- 繼承屬於擴充形式之一,但不一定是達到彈性設計的最佳方案;
- 在我們的設計,應該盡量對修改關閉,對擴充開發,無需修改現有代碼;
- 組合和委託可用於在運行時動態加上新的行為;
- 裝飾者可以在被裝飾者行為的前後根據實際情況加上自己的行為,必要時也可以將被裝飾者行為給替換掉;
- 可以用無數個裝飾者封裝一個組件,也就是說,裝飾者 A 封裝了被裝飾者 B ,裝飾者 C 再封裝裝飾者 A,根據實際情況這種行為可以累加到多層,通俗講就是套上多層外殼;
- 同時,被裝飾者也可以存在多個,也就是說 ConcreteComponent 這個角色也可以是多個的。
裝飾者模式的優點就是它的特點:可以在運行時動態,透明的為一個組件擴充功能,比繼承更加靈活;缺點也很明顯:它會導致設計中出現許多小對象,如果過度使用,會讓程式變得很複雜。
源碼下載
https://github.com/zhaozepeng/Design-Patterns/tree/master/DecoratorPattern
引用
https://en.wikipedia.org/wiki/Decorator_pattern
http://blog.csdn.net/jason0539/article/details/22713711
java/android 設計模式學習筆記(7)---裝飾者模式