設計模式(八)Context中的裝飾者模式

猥瑣發育_別浪發表於2019-03-27

一、基本概念:

1、定義:

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

2、應用場景:

  • 需要擴充套件一個類的功能。
  • 動態的為一個物件增加功能,而且還能動態撤銷。

3、通用類圖:

設計模式(八)Context中的裝飾者模式

  • 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();
    }
}
複製程式碼

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

三、Context中的裝飾模式

1、Context相關類圖:

設計模式(八)Context中的裝飾者模式

  • Context 對應的是裝飾者和被裝飾者都需要繼承的抽象類
  • ContextWrapper 對應的是裝飾者的基類
  • ContextImpl 對應的是被裝飾者,一些邏輯的真正處理的類
  • Activity、Service、Application對應的是裝飾者的具體實現類

2、Context

public abstract class Context {
  ......
  public abstract void startActivity(Intent var1);
  public abstract void startActivity(Intent var1, @RecentlyNullable Bundle var2);
  ......
}
複製程式碼

Context作為抽象基本,定義了四大元件啟動、獲取資源、類載入、檔案管理、許可權管理等等抽象方法

3、ContextImpl

class ContextImpl extends Context {
  ......
  @Override
  public void startActivities(Intent[] intents, Bundle options) {
    warnIfCallingFromSystemProcess();
    if ((intents[0].getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
      throw new AndroidRuntimeException(
          "Calling startActivities() from outside of an Activity "
              + " context requires the FLAG_ACTIVITY_NEW_TASK flag on first Intent."
              + " Is this really what you want?");
    }
    mMainThread.getInstrumentation().execStartActivities(
        getOuterContext(), mMainThread.getApplicationThread(), null,
        (Activity) null, intents, options);
  }
  ......
}
複製程式碼

ContextImpl作為被裝飾者,內部定義了Activity啟動等方法的具體實現

4、ContextWrapper:

public class ContextWrapper extends Context {
  Context mBase;

  public ContextWrapper(Context base) {
    mBase = base;
  }
  //獲取被裝飾者ContextImpl的引用
  protected void attachBaseContext(Context base) {
    if (mBase != null) {
      throw new IllegalStateException("Base context already set");
    }
    mBase = base;
  }
	......
  @Override
  public void startActivities(Intent[] intents) {
    mBase.startActivities(intents);
  }

  @Override
  public void startActivities(Intent[] intents, Bundle options) {
    //內部呼叫的還是ContextImpl的方法
    mBase.startActivities(intents, options);
  }
  ......
}
複製程式碼

ContextWrapper作為裝飾者的基類,持有被裝飾者ContextImpl的引用,並在自身的方法內部呼叫ContextImpl中的邏輯

5、Application Context 建立:

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方法建立了Application。

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物件傳入,然後對被裝飾者進行操作。

6、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中

7、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的傳入

相關文章