Android設計模式(二十)-裝飾模式

原文地址 http://blog.csdn.net/qq_25806863/article/details/69944273

裝飾模式也叫做包裝模式,是結構型設計模式之一。目的是爲了給一個類或對象增加行爲。可以是繼承的一種替代。

裝飾模式也很好理解,比如一個人,給他裝上火箭就能上天了,裝上潛水服就能下海了,但本身還是個人,人沒有任何變化。

定義

動態地給一個對象添加一些額外的職責,就增加對象功能來說,裝飾模式比生成子類實現更爲靈活。

使用場景

  • 需要拓展一個類的功能,增加附加職責時。
  • 需要動態增加功能,並動態刪除功能時。
  • 當不能使用繼承,但要提供繼承的功能時。

UML

這裏寫圖片描述

  • Component:抽象組件,可以是一個接口或抽象類,是被裝飾的原始對象
  • ConcreteComponent:組件的具體實現類。是被裝飾的具體對象。
  • Decorator:抽象的裝飾者。職責是裝飾被裝飾的對象。內部一定有一個對被裝飾者的引用。一般情況下也是一個抽象類,根據具體邏輯實現不同的子類。如果邏輯簡單可以直接是實現類。
  • ConcreteDecoratorA,B:具體的裝飾者。

先抽象組件類:

public abstract class Component {
    public abstract void operate();
}

組件的一個具體實現類,也就是被裝飾者者:

public class ConcreteComponent extends Component {
    @Override
    public void operate() {
        System.out.println("被裝飾者的操作");
    }
}

抽象的裝飾者,持有一個被裝飾者的引用:

public abstract 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 {
    public ConcreteDecoratorA(Component component) {
        super(component);
    }

    @Override
    public void operate() {
        operateA();
        super.operate();
        operateB();
    }

    private void operateA(){
        System.out.println("裝飾者A在被裝飾者的操作之前加些操作");
    }
    private void operateB(){
        System.out.println("裝飾者A在被裝飾者的操作之前後加些操作");
    }
}
public class ConcreteDecoratorB extends Decorator {
    public ConcreteDecoratorB(Component component) {
        super(component);
    }
    @Override
    public void operate() {
        operateA();
        super.operate();
        operateB();
    }

    private void operateA(){
        System.out.println("裝飾者B在被裝飾者的操作之前加些操作");
    }
    private void operateB(){
        System.out.println("裝飾者B在被裝飾者的操作之前後加些操作");
    }
}

客戶端調用:

public class Client {
    public static void main(String[] args) {
        Component component = new ConcreteComponent();
        ConcreteDecoratorA concreteDecoratorA = new ConcreteDecoratorA(component);
        ConcreteDecoratorB concreteDecoratorB = new ConcreteDecoratorB(component);

        concreteDecoratorA.operate();
        concreteDecoratorB.operate();
    }
}

輸出:
這裏寫圖片描述

裝飾類並沒有在原來的類上做審覈改動,只是拓展了一些操作。通過不同的包裝類就能拓展不同的功能。而傳入不同的被包裝類,也能拓展不同的具體對象。

簡單實現

拿一開始說的那個人爲例子。人是個抽象的概念。男孩是個具體的人。但是這個人要幹不同的事情要穿不一樣的衣服,就需要進行不同的包裝。

抽象的人:

public abstract class Person {
    public abstract void dress();
}

具體的人,也是原始的人,被裝飾者:

public class Boy extends Person {
    @Override
    public void dress() {
        System.out.println("穿內衣內褲");
    }
}

抽象的裝飾者:

public abstract class PersonDecorator {
    Person person;

    public PersonDecorator(Person person) {
        this.person = person;
    }

    public void dress(){
        person.dress();
    }
}

工作人裝飾者:

public class WorkPersonDecorator extends PersonDecorator {
    public WorkPersonDecorator(Person person) {
        super(person);
    }

    @Override
    public void dress() {
        super.dress();
        dressWork();
    }

    private void dressWork(){
        System.out.println("穿西裝領帶");
    }
}

運動的人裝飾者:

public class SportPersonDecorator extends PersonDecorator {
    public SportPersonDecorator(Person person) {
        super(person);
    }

    @Override
    public void dress() {
        super.dress();
        dressSport();
    }
    private void dressSport(){
        System.out.println("穿運動衣");
    }
}

客戶端調用:

public class Client {
    public static void main(String[] args) {
        Person boy = new Boy();
        System.out.println("包裝一個上班人:");
        WorkPersonDecorator workPersonDecorator = new WorkPersonDecorator(boy);
        workPersonDecorator.dress();
        System.out.println("包裝一個運動的人:");
        SportPersonDecorator sportPersonDecorator = new SportPersonDecorator(boy);
        sportPersonDecorator.dress();
    }
}

輸出:
這裏寫圖片描述

Android中的裝飾者模式

經常使用的Context其實用的就是包裝模式:

先看一下他們的繼承關係:

Activity的:
這裏寫圖片描述

然後是service的:
這裏寫圖片描述

然後是application的:
這裏寫圖片描述

他們都繼承了ContextWrapper類。根據這個名字就覺得這是一個裝飾類。裝飾類裏面會持有一個被裝飾者的引用,找一下:

package android.content;
public class ContextWrapper extends Context {
    Context mBase;

    public ContextWrapper(Context base) {
        mBase = base;
    }

    protected void attachBaseContext(Context base) {
        if (mBase != null) {
            throw new IllegalStateException("Base context already set");
        }
        mBase = base;
    }
    @Override
    public AssetManager getAssets() {
        return mBase.getAssets();
    }
    //.......
}

看到前面這兩行就感覺找對了。有兩種方法可以給mBase賦值。而且這個類的一切操作都像getAssets方法一樣,調用的是mBase的方法。因此可以知道,傳進來的mBase纔是真正的執行者。

接下來就找一下具體的被包裝類是什麼。

在分析Activity的onCreate方法調用時,知道activity的創建是在ActivityThread的performLaunchActivity方法中的,再來看一下:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        Activity activity = null;
        try {
            //創建Activity
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } 
//......
        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
            if (activity != null) {
                //創建Activity的Context
                Context appContext = createBaseContextForActivity(r, activity);
                //......
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window);

                //......
            }
            //......
        } 
        return activity;
    }

createBaseContextForActivity創建的是具體的Activity的Context,查看源碼發現是ContextImpl:

private Context createBaseContextForActivity(ActivityClientRecord r, final Activity activity) {
        //......這裏創建的是ContextImpl。
        ContextImpl appContext = ContextImpl.createActivityContext(
                this, r.packageInfo, r.token, displayId, r.overrideConfig);
        appContext.setOuterContext(activity);
        Context baseContext = appContext;
        //......
        return baseContext;
    }

在performLaunchActivity通過createBaseContextForActivity拿到一個ContextImpl之後,會調用activity.attach方法,看這個方法:

final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window) {
        attachBaseContext(context);

        //......
    }

都知道傳進來的是前面獲取的ContextImpl。

然後調用Activity的父類ContextThemeWrapper的attachBaseContext方法:

@Override
    protected void attachBaseContext(Context newBase) {
        super.attachBaseContext(newBase);
    }

看他有調用了父類的方法,ContextThemeWrapper的父類就是ContextWrapper了,然後就回到了ContextWrapper。

前面可以看到ContextWrapper剛好就有個attachBaseContext,給mBase賦值,因此可以說,至少在Activity上,ContextWrapper這個裝飾類裏面引用的具體被裝飾類是ContextImpl。其實Application和Service的被裝飾者也是ContextImpl。

package android.app;
class ContextImpl extends Context {
    //......
    @Override
    public AssetManager getAssets() {
        return getResources().getAssets();
    }
    //......
}

下面就可以畫出他們的關係了:
這裏寫圖片描述

總結

裝飾模式和前面的代理模式有點類似,容易把裝飾模式看成代理模式。裝飾模式是繼承的一種替代方案,主要爲所裝飾的對象增強功能,動態的增加方法。而代理模式主要是爲了控制對原有對象的訪問權限,不對原有對象進行功能增強。

我覺得兩者的區別主要是使用目的的區別。

優點

  • 是繼承的一種替代方案,但是比繼承要靈活的多,可以在運行時通過傳入不同的被裝飾器或不同的裝飾器來達成不同的行爲。
  • 增加新的被裝飾類和裝飾類很方便,而且不用修改原有代碼。便於拓展。符合開閉原則。

缺點

  • 設計模式基本都有這個缺點,就是會生成額外的類,增加系統複雜度。
  • 由於裝飾可以層層包裝,交叉包裝,如果包裝的很深的話,調試排錯會比較麻煩,也不容易理解。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章