从系统源码学设计模式-装饰模式

Posted 码上就好

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从系统源码学设计模式-装饰模式相关的知识,希望对你有一定的参考价值。

一、装饰模式介绍

  1. 装饰模式介绍

    动态地给一个对象添加一下额外的职责。就增加功能来说,装饰模式相比生成子类更为灵活。

  2. 装饰模式使用场景

    需要透明而且动态地扩展的功能的时候

  3. 装饰模式的UML类图

根据类图可以得出通用的模式代码如下:

(1)抽象组件类如下:

可以是一个接口或抽象类,其充当的就是被装饰的原始对象

从系统源码学设计模式-装饰模式

(2)组件具体实现类

该类是Component类的基本实现,也是我们装饰的具体对象

从系统源码学设计模式-装饰模式

(3)抽象装饰者

其承担的职责就是为了装饰我们的组件的对象,其内部一定要有一个指向组件对象的引用。在大多数情况下,该类为抽象类,需要根据不同的装饰逻辑实现不同的具体子类。当然,如果装饰逻辑单一,只有一个情况下我们可以省略该类直接作为具体的装饰者

从系统源码学设计模式-装饰模式

(4)装饰者具体实现类

只是对抽象装饰者做出具体的实现

public class ConcreteDecoratorA extends Decorator{ /** * 必要的构造方法,需要一个Component类型的对象 * * @param component */ public ConcreteDecoratorA(Component component) { super(component); }
@Override public void operate() { //装饰方法A和B既可以在父类方法前调用也可以在之后调用 operateA(); super.operate(); operateB(); }
/** * 自定义装饰方法A */ private void operateA() { //装饰方法逻辑... } /** * 自定义装饰方法B */ private void operateB() { //装饰方法逻辑... }}

(5)模拟客户调用

二、Android源码中的实现

我们以Context为例,介绍装饰模式在android源码中的应用。我们先大概了解一下Context类集成关系图,如下:

Context类是个抽象类,相当于我们的抽象组件Component,而在其内部定义了大量的抽象方法,比如startActivity。源码如下:

public abstract class Context {//.......省略一些代码.......public abstract void startActivity(@RequiresPermission Intent intent);
public abstract void startActivity(@RequiresPermission Intent intent, @Nullable Bundle options);//.......省略一些代码.......}

而真正的实现是在ContextImpl中完成的,ContextImpl集成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. A bug was existed between N and O-MR1 which allowed this to work. We // maintain this for backwards compatibility. final int targetSdkVersion = getApplicationInfo().targetSdkVersion;
if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0 && (targetSdkVersion < Build.VERSION_CODES.N || targetSdkVersion >= Build.VERSION_CODES.P) && (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); }//.......省略一些代码.......ContextThemeWrapper}

这里ContextImpl就相当于组件具体实现类,那么谁来充当装饰者的身份呢?从上面的Context集成图知道,Activity继承于ContextThemeWrapper

public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2, Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2, Window.OnWindowDismissedCallback, WindowControllerCallback, AutofillManager.AutofillClient, ContentCaptureManager.ContentCaptureClient { //.......省略一些代码....... }

ContextThemeWrapper集成于ContextWrapper

public class ContextThemeWrapper extends ContextWrapper { //.......省略一些代码.......
}

这个ContextWrapper就是我们要找的装饰者,在ContextWrapper中持有一个Context的引用如下:

public class ContextWrapper extends Context { @UnsupportedAppUsage Context mBase;
public ContextWrapper(Context base) { mBase = base; } //.......省略一些代码.......}

已经可以看出一点装饰者的样子了,但是好像缺点啥。对,就是来自组件的抽象方法,如上面提到的startActivity,再次查看源码:

public class ContextWrapper extends Context { //.......省略一些代码....... @Override public void startActivity(Intent intent) { mBase.startActivity(intent); }  @Override public void startActivity(Intent intent, Bundle options) { mBase.startActivity(intent, options); } //.......省略一些代码.......}

这里可以看到ContextWrapper中的startActivity方法也仅仅是简单的调用了具体组件实现类ContextImpl中对应的方法而已,实质上ContextWrapper中所有的方法都仅仅是调用了ContextImpl中对应的方法,是不是和我们上面说的装饰模式离职或者类图越来越像了?装饰模式应用的套路都是很相似的,对应具体的方法的包装扩展则是由ContextWrapper的具体的子类完成,比如我们的Activity、service、Application。这3个类的具体方法就不在给出了,可自行查看。


那么ContextImpl又是在何时何地被创建的呢?为什么上面我们会说装饰者leiContextWrapper中的context引用指向的是一个ContextImpl的实例呢?要搞清楚这些问题,首先要知道一个应用在哪启动的。在我们的ActivityThread中。

在处理启动Activity中,主要是调用handleLaunchActivity方法来处理启动的具体逻辑。在handleLaunchActivity方法中调用perforLaunchActivity方法来获取一个Activity的实例。


@Override public Activity handleLaunchActivity(ActivityClientRecord r, PendingTransactionActions pendingActions , Intent customIntent) //.......省略一些代码.......
final Activity a = performLaunchActivity(r, customIntent);
//.......省略一些代码....... }

查看performLaunchActivity代码

 /** Core implementation of activity launch. */ private Activity performLaunchActivity(ActivityClientRecord r , Intent customIntent) { //.......省略一些代码.......
ContextImpl appContext = createBaseContextForActivity(r); Activity activity = null; //.......省略一些代码....... activity = mInstrumentation.newActivity( cl, component.getClassName(), r.intent); //.......省略一些代码.......
appContext.setOuterContext(activity); //.......省略一些代码....... return activity; }

createBaseContextForActivity方法查看源码,如下:

 private ContextImpl createBaseContextForActivity(ActivityClientRecord r) { //.......省略一些代码.......
ContextImpl appContext = ContextImpl.createActivityContext( this, r.packageInfo, r.activityInfo , r.token, displayId, r.overrideConfig);
//.......省略一些代码....... return appContext; }

通过调用ContextImpl中的createActivityContext方法来创建一个ContextImpl的实例。并且通过setOuterContext方法将ContextImpl与Activity关联。

这就是我们系统源码中装饰模式的体现,是不是很简单~

以上是关于从系统源码学设计模式-装饰模式的主要内容,如果未能解决你的问题,请参考以下文章

浅析设计模式3 —— 装饰者模式

一起学设计模式状态模式+装饰器模式+简单工厂模式实战:提交个订单我到底经历了什么鬼?

23种设计模式学习之装饰者模式

前端也要学系列:设计模式之装饰者模式

重学设计模式(三设计模式-装饰器模式)

Swift学习之装饰者模式详解