Android 設計模式 - 裝飾器模式
簡介
在現實生活中可以看到很多裝飾模式的例子,或者可以大膽的說裝飾模式無處不在,就拿人來說,人需要各式各樣的衣著,不管你穿著怎樣,但是對於人的本質來說是不變的,充其量只是在外面披上一層遮羞物而已,這就是裝飾模式,裝飾物各不相同但是物件的本質是不變的.它的uml類圖如下:

裝飾模式的簡單實現
人總是要穿衣服,我們將人定義為一個抽象類,將其穿衣服的行為定義為一個抽象方法,show me the code.
public abstract class Person { /** * person下有一個穿著的抽象方法 */ public abstract void dressed(); }
public class Boy extends Person{ @Override public void dressed() { System.out.println("穿了內衣內褲"); } }
PersonCloth用來裝飾Person
public abstract class PersonCloth extends Person{ protected Person mPerson;//保持一個Person類的引用 public PersonCloth(Person person) { mPerson = person; } @Override public void dressed() { mPerson.dressed(); } }
下面兩個是繼承PersonCloth的實際裝飾類
public class CheapCloth extends PersonCloth{ public CheapCloth(Person person) { super(person); } public void dressShorts(){ System.out.println("穿條短褲"); } @Override public void dressed() { super.dressed(); } }
public class ExpensiveCloth extends PersonCloth{ public ExpensiveCloth(Person person) { super(person); } /** * 穿短袖 */ private void dressShirt(){ System.out.println("穿件短袖"); } /** * 穿皮衣 */ private void dressLeather(){ System.out.println("穿件皮衣"); } /** * 穿牛仔褲 */ private void dressJean(){ System.out.println("穿條牛仔褲"); } @Override public void dressed() { super.dressed(); dressShirt(); dressLeather(); dressJean(); } }
最終呼叫
public class Main { public static void main(String[] args){ //首先我們要有一個Boy Person person = new Boy(); //穿上便宜的衣服 CheapCloth cheapCloth = new CheapCloth(person); cheapCloth.dressed(); //穿上昂貴的衣服 ExpensiveCloth expensiveCloth = new ExpensiveCloth(person); expensiveCloth.dressed(); } }
Android原始碼中的裝飾模式
在Android中Context常常被稱為上帝物件,Context中定義了大量的抽象方法,如下
//....程式碼省略... public abstract void startActivity(@RequiresPermission Intent intent); public abstract void startActivity(@RequiresPermission Intent intent, @Nullable Bundle options); //....程式碼省略...
而Context的真正實現其實是在ContextImpl中完成,ContextImpl繼承自Context抽象類,並實現了Context的抽象方法.
class ContextImpl extends Context { //....程式碼省略... @Override public void startActivity(Intent intent) { warnIfCallingFromSystemProcess(); startActivity(intent, null); } @Override public void startActivity(Intent intent, Bundle options) { warnIfCallingFromSystemProcess(); // Calling start activity from outside an activity without FLAG_ACTIVITY_NEW_TASK is // generally not allowed, except if the caller specifies the task id the activity should // be launched in. if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0 && options != null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1) { throw new AndroidRuntimeException( "Calling startActivity() from outside of an Activity " + " context requires the FLAG_ACTIVITY_NEW_TASK flag." + " Is this really what you want?"); } mMainThread.getInstrumentation().execStartActivity( getOuterContext(), mMainThread.getApplicationThread(), null, (Activity) null, intent, -1, options); } //....程式碼省略... }
ContextImpl其實承擔的是元件的身份,那麼誰來承擔裝飾者的身份呢,其實只要繼承自ContextThemeWrapper的元件都是裝飾者的身份,例如Activity.
public class Activity extends ContextThemeWrapper
ContextThemeWrapper又繼承ContextWrapper
public class ContextWrapper extends Context { Context mBase; public ContextWrapper(Context base) { mBase = base; } }
其實看到這裡已經可以看到一些裝飾者模式了,ContextWrapper的startActivity其實就是呼叫的mBase,而mBase實際上就是ContextImpl的例項.
@Override public void startActivity(Intent intent) { mBase.startActivity(intent); }
總結
裝飾模式常常會和代理模式混淆,我們來說說這兩者的區別,裝飾模式是對物件功能的擴充套件,是繼承關係的一種替代方案(比繼承更加靈活),而代理模式則是給一個物件提供一個代理物件,並有代理物件來控制原有物件的引用。
裝飾模式應該為所有裝飾的物件增強功能;代理模式對代理的物件施加控制,但不對物件本身的功能進行增強
【附錄】

資料圖
需要資料的朋友可以加入Android架構交流QQ群聊:513088520
點選連結加入群聊【Android移動架構總群】: 加入群聊
獲取免費學習視訊,學習大綱另外還有像高階UI、效能優化、架構師課程、NDK、混合式開發(ReactNative+Weex)等Android高階開發資料免費分享。