理解裝飾者模式

引言

  Android面試經常被問到設計模式,而裝飾者模式被問到的概率尤其高。也許你在開發中或多或少遇到過類似這樣的代碼:

BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("fileName")));

這是裝飾者模式在JDK中的典型應用。設計模式的學習切忌走馬觀花,求大求全。否則容易理解不深,講不出所以然,在面試時遭遇尷尬。

定義

  裝飾者模式的書面定義是:“動態地給一個對象添加一些額外的職責。就增加功能來說,裝飾模式相比生成子類更爲靈活。”——語出《Android源碼設計模式解析與實戰》。裝飾者模式的UML類圖如下。

UML類圖

每一種設計模式都是一個微型的軟件系統,設計模式的UML類圖是對相應系統的高度概括。下圖的裝飾者模式類圖分爲藍色和綠色兩個部分。左上的藍色部分是被裝飾者,右下的綠色部分是裝飾者。
這裏寫圖片描述

一些基礎

  面向接口編程:將一個業務的定義和實現進行分離。比如人的穿衣這件事,分兩個部分來完成。

public interface IPerson{
    public void dress();
}
public class PersonImpl implements IPerson{
    @Override
    public void dress(){
        ...//方法體實現
    }
    public void fff(){
        ...
    }
    //其他方法
}

  第一個是定義一個接口(或者抽象類)IPerson,將人的共通的特性放在裏面,而某些特定的特性放在其實現類PersonImpl中。怎麼樣,是不是很有Java EE 搭建框架的感覺?這樣做的缺點是多了一個類,代碼增加了,但是大有裨益。一個顯而易見的好處是,當你在某處拿到一個IPerson引用時,你的IDE只會提示你IPerson裏面的方法,不會提示你PersonImpl中的fff()方法,這在API級別屏蔽了一些細節。

看圖說話

  於是我們順理成章地理解了,最初我們有左上角那兩個藍色的類。一個抽象類Component,一個是這個類的具體實現類ConcreteComponent,它們的本職是operate()。對應於人穿衣這件事就是,一個IPerson接口和PersonImpl類,它們的本職是dress()。如裝飾者模式的定義所言,爲了給被裝飾者增加一些額外的職責,我們需關注裝飾者部分,即圖中右下藍色部分。究竟它們是怎樣給被裝飾者添加額外的職責的,還是類比人穿衣打扮這件事的話,就是究竟是怎樣在人穿衣的前提下又增加其他裝飾物的?
  由於裝飾物有很多種,比如假髮、口紅、絲巾等等,所以依照面向接口的編程的思想,會抽取出共通的部分,放到基類中,見圖中Decorator類。由於我們需要知道裝飾者裝飾的到底是誰,所以裝飾物Decorator需要保存一個對本體的引用;又由於我們希望本體被裝飾後不改變其原有的行爲,不能披上絲巾就失去了穿褲子的能力,所以飾物Decorator需要繼承本體抽取出來的抽象類Component(或者實現Component接口)。詳見以下代碼:

public abtract class Decorator extends Component{
    private Component component;
    public Decorator(Component component){
        this.component=component;
    }
    @Override
    public void operate(){
        component.operate();
    }
}
public class ConcreteDecoratorA extends Decorator{
    ...
    @Override
    public void operate(){
        operateA1();
        super.operate();
        operateA12();
    }
    ...

  結合類圖和代碼可知,ConcreteComponent是本體,職責是operate(), ConcreteDecoratorA是裝飾者之一,ConcreteDecoratorA給ConcreteComponent增加了operateA1()、operateA2()兩個額外的職責。但是原有的operate()方法並沒有被隱藏或改變,只是其具體實現更豐富了。一個人本來只能穿衣服,後來他可以穿上衣服,並且打上領帶拿着公文包。
  聽起來很像代理模式?是的,很像很像。只是代理模式是在代理類中實例化被代理者對象,而裝飾者模式是在構造函數中傳入本體對象作爲引用;代理模式重點在隱藏被代理者的具體內容,而裝飾者重點在製造出一系列可插拔的裝飾物並且不改變本體的行爲 ,關注點不同。

裝飾者模式的應用

裝飾者模式在JDK中的應用

BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("fileName")));

  回到引言中提到的代碼,BufferedInputStream相當於裝飾者模式UML類圖中的ConcreteDecoratorA這個角色,而FileInputStream相當於ConcreteComponent角色。Decorator角色的正確理解是:它並不是純粹的裝飾者,它是裝飾本體後的對象。按人-衣物的例子來說,Decorator表示穿上某件衣服飾品的人,而不是飾品本身,這和實際生活有細微的差別。實際生活中,裝飾品可以單獨存在。你穿,或者不穿,衣服飾品就在那,不多不少。而裝飾者模式中,裝飾者這個角色必須要在構造函數中傳入一個本體,即它所裝飾的東西,要不然它沒有什麼實際意義。ConcreteComponent是你,ConcreteDecoratorA是穿上衣服A的你,而不是A那件衣服本身。ConcreteDecoratorA是爲“可穿衣服的的東西”量身定做,它可以給你穿,給路人甲穿,甚至給狗穿。但是不能給樹穿給天空穿,儘管實際生活中可以。
  

裝飾者模式在Android源碼中的應用

  當我參照資料畫出下面的UML類圖,我被自己嚇了一跳!我們在Android開發中每天都見到的Activity原來是個裝飾者,其裝飾的本尊是ContextImpl,那些常用的方法,諸如startActivity,是在ContextImpl中具體實現的。而且不止有Activity,Application和Service都是ContextImpl的裝飾者,因此你可以在Application中啓動一個Activity,也可以在Service中啓動。
這裏寫圖片描述
  Context這個Android開發中的上帝對象,在本文中就不展開論述。
  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章