裝飾模式:Decorator Pattern

裝飾模式簡介

裝飾模式(Decorator Pattern)也被稱爲包裝模式(Wrapper Pattern),是結構型設計模式之一,能夠在不改變一個對象自己功能的基礎上給對象增長額外的新行爲,以對客戶端透明的方式來動態擴展對象的功能(注意,並非改變對象本質),同時也是一種很好的替代繼承關係的方案。在現實生活中也能夠看到不少裝飾模式的例子。java

生活中的例子

穿衣服是使用裝飾的一個例子。 以爲冷時, 你能夠穿一件毛衣。 若是穿毛衣還以爲冷, 你能夠再套上一件夾克。 若是遇到下雨, 你還能夠再穿一件雨衣。 全部這些衣物都 「擴展」 了你的基本行爲, 但它們並非你的一部分, 若是你再也不須要某件衣物, 能夠方便地隨時脫掉。編程

代碼實現

人老是要穿衣服的,將人定義爲抽象類,穿衣服的行爲定義爲一個抽象方法:設計模式

/** * 須要被裝飾類的基類 */
public abstract class Person {
  
  public abstract void dressed();
}

/** * 具體須要被裝飾的"對象" */
public class Boy extends Person {
  
  @Override
  public void dressed(){
    System.out.println("穿了內衣");
  }
}
複製代碼

Boy 類繼承自 Person,該類僅對 Person 中的 dressed 方法做了具體實現,Boy 類則是咱們所要裝飾的具體「對象」,如今須要一個裝飾者來裝飾 Boy 對象。app

/** * 裝飾器基類 */
public abstract class PersonCloth extends Person {
  
  protected Person mPerson;// 保持一個對 Person 對象的引用
  
  public PersonCloth(Person mPerson) {
    this.mPerson = mPerson;
  }
  
  @Override
  public void dressed(){
    mPerson.dressed();
  }
}

/** * 裝飾器一 */
public class ExpensiveCloth extends PersonCloth {
  
  public ExpensiveCloth(Person person) {
    super(person);
  }
  
  @Override
  public void dressed(){
    super.dressed();
    dressLeather();
    dressJean();
  }
  
  private void dressLeather() {
    System.out.println("穿件皮衣");
  }
  
  private void dressJean() {
    System.out.println("穿條牛仔褲");
  }
}

/** * 裝飾器二 */
public class CheapCloth extends PersonCloth {
  
  public CheapCloth(Person person) {
    super(person);
  }
  
  @Override
  public void dressed(){
    super.dressed();
    dressShorts();
  }
  
  private void dressShorts() {
    System.out.println("穿條短褲");
  }
}
複製代碼

客戶端調用:編程語言

public class Client {
  
  public static void main(String[] args) {
    Person person = new Boy();
    PersonCloth clothCheap = new CheapCloth(person);
    // clothCheap.dressed();
    PersonCloth clothExpensive = new ExpensiveCloth(clothCheap);
    // PersonCloth clothExpensive = new ExpensiveCloth(new CheapCloth(person));
    clothExpensive.dressed();
  }
}
複製代碼

輸出:ide

穿了內褲
穿條短褲
穿件皮衣
穿條牛仔褲
複製代碼

裝飾模式結構

  • Component 聲明封裝器和被封裝對象的公用接口;
  • Concrete Component 類是被封裝對象所屬的類。 它定義了基礎行爲, 但裝飾類能夠改變這些行爲;
  • Base Decorator 類擁有一個指向被封裝對象的引用成員變量。 該變量的類型應當被聲明爲通用部件接口, 這樣它就能夠引用具體的部件和裝飾。 裝飾基類會將全部操做委派給被封裝的對象;
  • Concrete Decorators 定義了可動態添加到部件的額外行爲。 具體裝飾類會重寫裝飾基類的方法, 並在調用父類方法以前或以後進行額外的行爲;
  • Client 可使用多層裝飾來封裝部件, 只要它能使用通用接口與全部對象互動便可。

裝飾模式適用性

  • 若是你但願在無需修改代碼的狀況下便可使用對象, 且但願在運行時爲對象新增額外的行爲, 可使用裝飾模式。
  • 裝飾能將業務邏輯組織爲層次結構, 你可爲各層建立一個裝飾, 在運行時將各類不一樣邏輯組合成對象。 因爲這些對象都遵循通用接口, 客戶端代碼能以相同的方式使用這些對象。
  • 若是用繼承來擴展對象行爲的方案難以實現或者根本不可行, 你能夠考慮該模式。
  • 許多編程語言使用 final關鍵字來限制對某個類的進一步擴展。 複用最終類已有行爲的好方法一般就是使用裝飾模式: 用封裝器對其進行封裝。

裝飾模式優缺點

  • 優勢
    • 無需建立新子類便可擴展對象的行爲;
    • 能夠在運行時添加或刪除對象的功能;
    • 能夠用多個裝飾封裝對象來組合幾種行爲;
    • 較好的遵循單一職責原則。 能夠將實現了許多不一樣行爲的一個大類拆分爲多個較小的類。
  • 缺點
    • 在封裝器棧中刪除特定封裝器比較困難;
    • 實現行爲不受裝飾棧順序影響的裝飾比較困難;

與其餘設計模式比較

  • 組合模式this

    • 組合和裝飾的結構圖很類似, 二者都依賴遞歸組合來組織無限數量的對象;
    • 裝飾只有一個子組件。 此外,裝飾爲被封裝對象添加了額外的職責, 組合對其子節點的結果進行了 「求和」。
  • 責任鏈模式spa

    • 二者也是依賴遞歸組合將須要執行的操做傳遞給一系列對象;
    • 責任鏈的管理者能夠相互獨立地執行一切操做, 還能夠隨時中止傳遞請求。但裝飾沒法中斷請求的傳遞。
  • 策略模式設計

    • 裝飾可以讓你更改對象的外表, 策略模式則讓你可以改變其本質。

真僞裝飾模式的討論

「在一個真正的裝飾模式中,每個裝飾者都不容許修改被裝飾對象的行爲,只能擴展其功能。從代碼的角度講,每個裝飾者在重寫的某個方法中都要直接或間接調用到被裝飾對象的實現方法,不然不能稱之爲裝飾模式。」code

實例

Android 中 Context、Contextwrapper、Activity 等結構就使用了裝飾模式

/** * Interface to global information about an application environment. This is * an abstract class whose implementation is provided by the Android system. * ... */
public abstract class Context {
  
  public abstract Resources getResources();
  ...
}

/** * Common implementation of Context API, which provides the base * context object for Activity and other application components. */
class ContextImpl extends Context {
  
  	@Override
    public Resources getResources() {
        return mResources;
    }
    ...
}

/** * ... Can be subclassed to modify behavior without changing * the original Context. */
public class ContextWrapper extends Context {
    Context mBase;

    public ContextWrapper(Context base) {
        mBase = base;
    }
  
    @Override
    public Resources getResources() {
        return mBase.getResources();
    }
    ...
}

/** * A context wrapper that allows you to modify or replace the theme of the * wrapped context. */
public class ContextThemeWrapper extends ContextWrapper {
    private int mThemeResource;
    private Resources.Theme mTheme;
    private LayoutInflater mInflater;
    private Configuration mOverrideConfiguration;
    private Resources mResources;
  
    @Override
    public Resources getResources() {
        if (mResources == null) {
            if (mOverrideConfiguration == null) {
                mResources = super.getResources();
            } else {
                final Context resContext =    createConfigurationContext(mOverrideConfiguration);
                mResources = resContext.getResources();
            }
        }
        return mResources;
    }
}

public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2, Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2, Window.OnWindowDismissedCallback, WindowControllerCallback, AutofillManager.AutofillClient {
        ...  
}
複製代碼

參考

Article by Panxc

相關文章
相關標籤/搜索