设计模式知识梳理(4) - 结构型 - 装饰模式
2018-11-26 本文已影响62人
泽毛
一、基本概念
1.1 定义
装饰模式使用一种对客户端透明的方式来 动态地扩展对象的功能,同时它是继承关系的一种替代方案,其包含以下四种角色:
-
Component
:抽象组件。可以是一个接口或抽象类,其充当的就是被装饰的原始对象。 -
ConcreteComponent
:组件具体实现类。Component
类的基本实现,也是我们装饰的具体对象。 -
Decorator
:抽象装饰者。用于装饰组件对象,内部 一定有一个指向组件对象的引用,大多数情况下该类为抽象类,需要根据不同的装饰逻辑实现不同的具体子类。 -
ConcreteDecoratorA
:装饰者具体实现类。
1.2 例子
- 定义抽象组件,其包含抽象方法
operate()
。
/**
* 抽象组件类。
*
* @author lizejun
**/
public abstract class Component {
/**
* 抽象组件类的抽象方法。
*/
public abstract void operate();
}
- 定义组件具体实现类。
/**
* 组件具体实现类。
*
* @author lizejun
**/
public class ConcreteComponent extends Component {
@Override
public void operate() {}
}
- 定义抽象装饰者。
/**
* 抽象装饰者。
*
* @author lizejun
**/
public class Decorator extends Component {
private Component mComponent;
public Decorator(Component component) {
mComponent = component;
}
@Override
public void operate() {
mComponent.operate();
}
}
- 定义装饰者具体实现类,它包含了两个装饰方法,该方法可以在父类方法的前后调用,为装饰对象 增强功能。
/**
* 装饰者具体实现类。
*
* @author lizejun
**/
public class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
@Override
public void operate() {
//在父类之前调用装饰方法。
operateBefore();
super.operate();
//在父类之后调用装饰方法。
operateAfter();
}
/**
* 装饰方法 A。
*/
private void operateBefore() {
}
/**
* 装饰方法 B。
*/
private void operateAfter() {
}
}
1.3 应用场景
- 动态地为一个类新增或删除功能。
- 不能使用继承,但要提供继承的功能。
1.4 优缺点
优点
- 可以通过动态地选择不同的 装饰器(被装饰者),来实现不同的功能。
- 通过 组合替代继承,避免造成的无限扩张。
- 装饰者和被装饰者 解耦,新增装饰者功能时无需改变被装饰者的代码,符合开闭原则。
缺点
- 当要修改抽象组件的时候,装饰者的代码也可能需要修改。
二、Android 源码
在Android
源码当中,有一个装饰模式的典型应用 Context
,它的类图我们在之前分析SP
的内部实现原理的时候也有分析过, Android 数据存储知识梳理(3) - SharedPreference 源码解析:
Context
是一个抽象类,在其中定义了我们常用的大量抽象方法,它对应于我们上面谈到的 抽象组件。
public abstract class Context {
public abstract void startActivity(@RequiresPermission Intent intent);
public abstract void sendBroadcast(@RequiresPermission Intent intent);
public abstract ComponentName startService(Intent service);
}
其真正的实现是在ContextImpl
当中,它继承自Context
抽象类,它的角色是 组件的具体实现类。
class ContextImpl extends Context {
@Override
public void startActivity(Intent intent) {
warnIfCallingFromSystemProcess();
startActivity(intent, null);
}
@Override
public void sendBroadcast(Intent intent) {
warnIfCallingFromSystemProcess();
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
intent.prepareToLeaveProcess(this);
ActivityManager.getService().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
@Override
public ComponentName startService(Intent service) {
warnIfCallingFromSystemProcess();
return startServiceCommon(service, false, mUser);
}
}
而上面谈到的装饰者就是最常见的Activity
、Service
和Application
这三个,它们有一个共同的基类ContextWrapper
,而在ContextWrapper
中持有一个指向具体抽象对应的引用mBase
,当调用Context
定义的抽象方法时,其实是由mBase
来完成的。
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 void startActivity(Intent intent) {
mBase.startActivity(intent);
}
那么mBase
是在哪里赋值的呢?
我们以Activity
为例,看一下ActivityThread
当中的代码,当启动一个Activity
的时候,必然需要走到performLaunchActivity
方法,这里会创建ContextImpl
和Activity
对象,最后通过setOuterContext
和attach
方法建立双向的关联,其中Activity#attach
就会给基类中的mBase
变量赋值。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//为 Activity 创建 ContextImpl 对象。
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
//创建 Activity。
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);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
//ContextImpl 和 Activity 建立双向的关联关系。
appContext.setOuterContext(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);
}
三、项目应用
待补充。
四、参考文献
- <<
Android
源码设计模式 - 解析与实战>>