1. 程式人生 > >裝飾模式和Context類結構

裝飾模式和Context類結構

一、基本概念:

1、定義:動態地給一個物件新增一些額外的職責。裝飾模式比子類就增加功能來說更為靈活。

2、應用場景:
* 需要擴充套件一個類的功能。
* 動態的為一個物件增加功能,而且還能動態撤銷。

3、缺點:
多層裝飾者存在的層級複雜的情況

4、通用類圖:
這裡寫圖片描述
* Component:抽象類或者介面,這是裝飾者和被裝飾者都需要實現的介面或者繼承的基類。
* ConcreteComponent:被裝飾者的具體構件
* Decorate:裝飾者角色,一般是一個抽象類,定義一個private變數指向Component。
* ConcreteDecotator:裝飾者具體實現類,一些在方法中實現一些功能的新增

二、示例:

1、Component:
定義了一個介面,定義了一個sing的方法

public interface Component {
    void sing();
}

2、ConcreteComponent:
定義了具體的被裝飾者,實現了Component介面

public class ConcreteComponent implements Component {
    @Override
    public void sing() {
        System.out.print("sing....");
    }
}

3、Decorator:
定義了抽象裝飾者,內部持有被裝飾者的引用,才能操作被裝飾者

public abstract class Decorator implements Component {
    private Component mComponent;

    public Decorator(Component component) {
        this.mComponent = component;
    }

    @Override
    public void sing() {
        mComponent.sing();
    }
}

4、ConcreteDecorator:
定義了具體的裝飾者,可以在方法內做一些功能擴充套件的工作。

public class ConcreteDecorator extends Decorator {

    public ConcreteDecorator(Component component) {
        super(component);
    }


    //先交錢,後唱歌(添加了收費的功能)
    @Override
    public void sing() {
        System.out.print("先交錢....");
        super.sing();
    }
}

5、測試:

public class DecoratorTest {

    public static void main(String[] args){
        Component component = new ConcreteComponent();
        Decorator decorator = new ConcreteDecorator(component);
        decorator.sing();
    }
}

結論:
這裡寫圖片描述

6、更換裝飾者:

public class ConcreteDecorator2 extends Decorator{
    public ConcreteDecorator2(Component component) {
        super(component);
    }

    @Override
    public void sing() {
        System.out.print("奏樂....");
        super.sing();
    }
}
public class DecoratorTest {

    public static void main(String[] args){
        Component component = new ConcreteComponent();
        Decorator decorator = new ConcreteDecorator2(component);
        decorator.sing();
    }
}

更換了一個裝飾者,提供了一些其他功能,方便裝飾者的更換。

三、Android中裝飾模式

1、Context相關類圖:
這裡寫圖片描述

  • Context 對應的是裝飾者和被裝飾者都需要繼承的抽象類
  • ContextWrapper 對應的是裝飾者的基類
  • ContextImpl 對應的是被裝飾者
  • Activity、Service、Application對應的事裝飾者的具體實現類

2、ContextWrapper:

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;
    }
    ......
}

裝飾者ContextWrapper持有被裝飾者Context的引用。雖然不是抽象類,但主要負責Context的方法呼叫,可以將ContextWrapper理解為是一個裝飾者的基類。
問題:被裝飾的Context 什麼時候建立,如何通過attachBaseContext 將其傳入。

3、Application Context 建立:
應用程式啟動最後是通過ActivityThread去啟動Activity
ActivityThread:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

    ......
    Application app = r.packageInfo.makeApplication(false, mInstrumentation);
    ......
}

通過呼叫LoadedApk的makeApplication方法建立了Application例項物件。

LoadedApk:

public Application makeApplication(boolean forceDefaultAppClass,
                                   Instrumentation instrumentation) {
    ......
    ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
    app = mActivityThread.mInstrumentation.newApplication(
            cl, appClass, appContext);
    appContext.setOuterContext(app);
    ......
}

建立了被裝飾者ContextImpl物件,然後呼叫了Instrumentation的newApplication方法。

Instrumentation:

 static public Application newApplication(Class<?> clazz, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        Application app = (Application)clazz.newInstance();
        app.attach(context);
        return app;
    }

通過呼叫Application(裝飾者)的attach方法,將ContextImpl物件傳入。

final void attach(Context context) {
    attachBaseContext(context);
    mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
}

通過呼叫裝飾基類ContextWrapper的attachBaseContext方法來將ContextImpl物件傳入,然後對被裝飾者進行操作。

4、Activity Context的建立:

ActivityThread:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ......
    //(1)建立了ContextImpl
    ContextImpl appContext = createBaseContextForActivity(r);
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = appContext.getClassLoader();
        //(2) 建立Activity物件
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
        ......

        if (activity != null) {
            appContext.setOuterContext(activity);
            //(3)將ContextImpl傳入到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, r.configCallback);

            ......

    return activity;
}

呼叫了Activity的attach方法:
Activity:

  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, ActivityConfigCallback activityConfigCallback) {

        attachBaseContext(context);
        ......
}

完成了ContextImpl的建立,並傳入到Activity中

5、Service Context的建立

Service的啟動最後呼叫到了ActivityThread的handleCreateService方法
ActivityThread:

private void handleCreateService(CreateServiceData data) {
    ......
    Service service = null;
    try {
        java.lang.ClassLoader cl = packageInfo.getClassLoader();
        //(1)service建立
        service = (Service) cl.loadClass(data.info.name).newInstance();

        ......
        //(2)ContextImpl建立
        ContextImpl context = ContextImpl.createAppContext(this, packageInfo);
        context.setOuterContext(service);

        Application app = packageInfo.makeApplication(false, mInstrumentation);
        //(3)ContextImpl傳入
        service.attach(context, this, data.info.name, data.token, app,
                ActivityManager.getService());
        service.onCreate();
        ......
    }
}

Service:

public final void attach(
        Context context,
        ActivityThread thread, String className, IBinder token,
        Application application, Object activityManager) {
    attachBaseContext(context);
    ......
}

完成了Service建立和ContextImpl的傳入