Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> java/android 設計模式學習筆記(7)---裝飾者模式

java/android 設計模式學習筆記(7)---裝飾者模式

編輯:關於Android編程

這篇將會介紹裝飾者模式(Decorator Pattern),裝飾者模式也稱為包裝模式(Wrapper Pattern),結構型模式之一,其使用一種對客戶端透明的方式來動態的擴展對象的功能,同時它也是繼承關系的一種替代方案之一,但比繼承更加靈活。在現實生活中也可以看到很多裝飾者模式的例子,或者可以大膽的說裝飾者模式無處不在,就拿一件東西來說,可以給它披上無數層不一樣的外殼,但是這件東西還是這件東西,外殼不過是用來擴展這個東西的功能而已,這就是裝飾者模式,裝飾者的這個角色也許各不相同但是被裝飾的對象本質是不變的。
  我們的目標是允許類統一擴展,在不修改現有代碼的情況下,就可搭配新的行為。如能實現這樣的目標,有什麼好處呢?這樣的設計具有彈性,可以應對改變,可以接受新的功能來應對改變的需求,也就是 OO 原則中的對擴展開放和對修改關閉的開閉原則。

特點

動態地給一個對象添加一些額外的職責,就增加功能來說,裝飾者模式相比生成子類更加靈活,提供了有別於繼承的另一種選擇。
  裝飾者模式可以靜態的,或者根據需要可以動態的在運行時為一個對象擴展功能。被裝飾者和眾多的裝飾者都是繼承自一個接口,他們有著一樣的行為特性。裝飾者模式是繼承的另一種選擇方式,繼承是在編譯的時候為類添加新的行為,並且這個改變會影響所有原來該類的實體,裝飾者模式就不一樣,它提供一種能夠在運行時根據需要選擇不同運行對象的功能。裝飾者模式和繼承這兩種方式的不同之處在某些擴展功能的情況下顯得尤為重要,在一些面向對象編程的語言中,類無法在運行時被創建,而且當需要擴張功能時,這些行為往往無法預測,這就意味著在每個可能的情況下,這個類都需要被創建,所以對比之下,裝飾者模式優點在於每個裝飾者都是對象,在運行時被創建,並且能夠在每次使用時根據需要自己組合。

UML類圖

我們現在來看看裝飾者模式的 uml 類圖:
  這裡寫圖片描述
裝飾者模式共有四大角色:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCkNvbXBvbmVudKO6s+nP89fpvP6/ydLUysfSu7j2vdO/2rvy1d/Kx7Ppz/PA4KOsxuSz5LWxtcS+zcrHsbvXsMrOtcTUrcq8ttTP86Os08PAtLao0uXXsMrO1d+6zbG717DKztXftcS7+bG+0NDOqqGjQ29uY3JldGVDb21wb25lbnSjutfpvP6+38zlyrXP1sDguMPA4MrHIENvbXBvbmVudCDA4LXEu/mxvsq1z9ajrNKyysfO0sPH17DKzrXEvt/M5bbUz/Oho0RlY29yYXRvcqO6s+nP89ewys7V39ewys7X6bz+ttTP86OsxuTE2rK/0ru2qNKq09DSu7j21rjP8tfpvP621M/ztcTS/dPDoaPU2rTztuDK/cfpv/bPwqOsuMPA4M6qs+nP88Dgo6zQ6NKquPm+3bK7zay1xNewys7C37ytyrXP1rK7zay1xL7fzOXX08DgoaO1sci7o6zI57n7ysfXsMrOwt+8rbWl0rujrNa709DSu7j2tcTH6b/2z8LO0sPHv8nS1Lr2wtS4w8Dg1rG909f3zqq+38zltcTXsMrO1d+ho0NvbmNyZXRlRGVjb3JhdG9yQSC6zSBDb25jcmV0ZURlY29yYXRvckKjutewys7V377fzOXKtc/WwOC21LPpz/PXsMrO1d+1xL7fzOXKtc/WoaOhoaGh1NrS0dPQtcQgQ29tcG9uZW50ILrNIENvbmNyZXRlQ29tcG9uZW50IMzlz7XPwqOsyrXP1tewys7V38Sjyr3AtMCp1bnUrdPQz7XNs7XEuabE3KOsv8nS1LfWzqogNSC49rK91ui8zLPQu/LV38q1z9YgQ29tcG9uZW50INfpvP6jrMn6s8nSu7j2IERlY29yYXRvciDXsMrO1d+z6c/zwOCju9TayfqzybXE1eK49iBEZWNvcmF0b3Ig17DKztXfwODW0KOs1Pa809K7uPYgQ29tcG9uZW50ILXEy73T0LPJ1LG21M/zo7u9qyBDb25jcmV0ZUNvbXBvbmVudCC78tXfxuTL+9Do0qqxu9ewys61xLbUz/O0q8jrIERlY29yYXRvciDA4NbQsqK4s9a1uPjJz9K7sr21xCBDb21wb25lbnQgttTP86O71NrXsMrO1d8gRGVjb3JhdG9yIMDg1tCjrL2ry/nT0LXEstnX97a8zOa7u7PJuMMgQ29tcG9uZW50ILbUz/O1xLbU06ay2df3o7vU2iBDb25jcmV0ZURlY29yYXRvciDA4NbQo6y4+b7d0OjSqrbU06a4srjH0OjSqtbY0LS1xLe9t6iho6GhoaHXsMrO1d/Eo8q91NrUtMLr1tDTw7XE0rLKx7fHs6O24LXEo6zU2iBKYXZhILrNIEFuZHJvaWQg1tC2vMTcubu8+7W917DKztXfxKPKvbXE07DX06O6DQo8cD4mbmJzcDs8L3A+DQo8aDIgaWQ9"java-中的裝飾者模式">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 window
com.android.decoratorpattern E/shawn: then drawing the horizontal scroll bar
com.android.decoratorpattern E/shawn: window description : a window with horizontal scroll bar
com.android.decoratorpattern E/shawn: drawing a window
com.android.decoratorpattern E/shawn: then drawing the vertical scroll bar
com.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

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved