定义
装饰模式又叫包装模式,可以动态的给一个对象添加一些额外的职责。装饰模式比生成子类更灵活,是继承关系的替代方案之一。
UML类图
如上,主要有被装饰者和装饰者角色,分别如下
-
Component
抽象组件,被装饰对象的抽象,可以是接口或者抽象类。
-
ConcreteComponent
Component的实现类,具体被装饰的对象。
-
Decoator
抽象装饰者,该类继承自Component组件,并且内部持有一个组件对象的引用。负责装饰组件对象。
-
ConcreteDecorator
具体的装饰者,负责实现具体的装饰方法。
示例
装饰模式最简单常见的例子就是人的着装打扮。我们可以每天穿不同的衣服对自己进行打扮,但是被打扮的人还是不变的,我们还是我们,只是穿了不同的衣服。
/**
* 抽象组件Component,被装饰的原始类
* 定义抽象的人
*/
public abstract class People {
public abstract void dress();
}
/**
* 实现类ConcreteComponent,具体要被装饰的类
* 定义具体的男孩
*/
public class Boy extends People {
@Override
public void dress() {
System.out.println("男孩穿衣服");
}
}
/**
* 抽象装饰类Decoator
* 该类中保存了一个被装饰类的引用,方便调用被装饰类的方法
* 定义衣服的抽象
*/
public abstract class PersonDecorate extends People {
private People people;
public PersonDecorate(People people) {
this.people = people;
}
@Override
public void dress() {
people.dress();
}
}
/**
* 具体装饰类ConcreteDecorator
* 添加实现具体的扩展功能,比如这里是dressWorkClothe方法
* 并且在原方法中整合调用
*/
public class WorkClothes extends PersonDecorate {
public WorkClothes(People people) {
super(people);
}
@Override
public void dress() {
super.dress();
dressWorkClothe();
}
private void dressWorkClothe(){
System.out.println("穿职业装");
}
}
/**
* 客户端使用
* 实例化Boy对象,并将其传入到WorkClothes对象中
* 调用WorkClothes的dress方法
*/
public static void main(String args[]){
Boy boy = new Boy();
WorkClothes workClothes = new WorkClothes(boy);
workClothes.dress();
}
男孩穿衣服
穿职业装
Android源码中的装饰模式
我们在安卓开发中最常使用的Context类就是装饰模式的经典实现。
首先看一下Context类
public abstract class Context {
//...
/**
* Same as {@link #startActivity(Intent, Bundle)} with no options
* specified.
*
* @param intent The description of the activity to start.
*
* @throws ActivityNotFoundException
*`
* @see #startActivity(Intent, Bundle)
* @see PackageManager#resolveActivity
*/
public abstract void startActivity(@RequiresPermission Intent intent);
//...
}
Context中定义了大量我们平时开发中常用的方法,例如startActivity等,但是Context是一个抽象类。其具体实现是由ContextImpl完成的
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);
}
}
ContextImpl就相当于装饰模式中的抽象组件的具体实现类。我们知道Activity本质上也是Context,看一下Activity的继承结构
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback, WindowControllerCallback,
AutofillManager.AutofillClient {
// ...
}
Activity并没有直接继承自Context,而是继承自ContextThemeWrapper,而ContextThemeWrapper又继承自ContextWrapper,ContextWrapper最终继承自Context
public class ContextWrapper extends Context {
Context mBase;
public ContextWrapper(Context base) {
mBase = base;
}
@Override
public void startActivity(Intent intent) {
mBase.startActivity(intent);
}
// ...
}
这里ContextWrapper对象持有Context实现类ContextImpl对象的一个引用mBase,调用了 mBase.startActivity(intent)方法。那么这个ContextWrapper对象就是装饰者对象,并且调用了被装饰者ContextImpl的方法。