简介
装饰模式也称为包装模式,使用一种对客户端透明的方式来动态扩展对象的功能,同时也是继承关系的一种替代方案。日常生活中很多装饰模式的例子,我们穿着衣服就是对我们自己的一种装饰。
定义
动态地将责任附加到对象上。若要扩展功能,装饰着提供比继承更加有弹性的替代方案。
使用场景
- 需要透明并且动态地扩张类的功能;
- 当不能采用继承的方式对系统扩展或者不采用继承不利于系统扩展和维护时;
UML类图
Component:抽象构件
接口或抽象类,被装饰的原始对象。ConcreteComponent:具体构件
抽象构件的基本实现,是我们要装饰的具体对象。Decorator:抽象装饰类
我们的抽象装饰类,用于给具体构件增加职责,但是具体的职责在它的子类中实现。ConcreteDecorator:具体装饰类
抽象装饰类的子类,负责向构件添加新的职责。
装饰模式简单实现
以人穿衣服为实例demo来实现如下:
点击查看
透明装饰模式
我们看看客户端的调用代码:
public class Main {
public static void main(String[] args){
Person person,personWrap;
person = new XiaoYe();
personWrap = new PersonClothWrapper(person);
personWrap.dress();
}
}
我们具体类和要包装的类都可以通过定义Person接口来进行访问。客户端可以完全针对接口抽象编程。这是透明装饰模式的一种使用。
透明装饰模式可以让客户透明的使用装饰之前的对象和装饰之后的对象,无需关心它们的区别,此外,还可以对一个已经装饰过的对象多次装饰,得到更加复杂的对象。
半透明装饰模式
上面我们看了透明的方式来指定装饰者和被装饰者,现在如果我们想要客户端调用装饰者的额外添加的方法,使用上面那种方式是不能实现的,那么客户端在定义装饰者的时候要为具体的装饰者对象了,这样装饰者才可以调用额外添加的方法。
半透明装饰模式可以给系统带来更多的灵活性。但是缺点是不能对一个对象进行多次装饰。(不知道为啥)
Android中装饰模式的使用
Context实现
我们看看我们常用的Context类的这个系统的源码,它的整套设计就是一个装饰模式,基本的类图如下:
仔细一看Context的设计模式真的完全遵循装饰设计模式。
Context相关问题
我们在Activity中可以使用Context来做很多事情,对于Context我们有必要了解它的来龙去脉。
- Activity的Context啥时候创建的,如何创建的?
Activity的启动流程我们这里略过,看到ActivityThread的perfromLauncheActvity方法,从启动一个Activity开始看Activity的Context是如何关联上的。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
...
try {
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
...
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);
...
return activity;
}
看几个关键的代码,通过createBaseContextForActivity创建Context对象,然后通过appContext.setOuterContext(activity);在Context里面关联Activity然后调用activity.attach方法传入Context对象。
- Application的Context啥时候创建,如何创建?
就本地而言所有的这些操作都是从ActivityThread的内部的Handler H开始分发下来,看到handleBindApplication方法。
private void handleBindApplication(AppBindData data) {
...
try {
// If the app is being launched for full backup or restore, bring it up in
// a restricted environment with the base application class.
Application app = data.info.makeApplication(data.restrictedBackupMode, null);
mInitialApplication = app;
...
}
看到makeApplication方法里面:
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
if (mApplication != null) {
return mApplication;
}
...
Application app = null;
...
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
...
mActivityThread.mAllApplications.add(app);
mApplication = app;
...
return app;
}
通过ContextImpl创建Context,调用Context的setOuterContext关联我们的Application,在创建Application的时候传入appContext对象,我们的Application可以使用Context了。
- Service 的Context也是一样通过调用handleCreateService方法,使用上面的方式创建和绑定Context对象。
- 应用中的Context数量?
Application,Activity,Service一个实例都创建一个Context对象。其余两大组件没有创建Context它们间接的使用Context。
总结
装饰模式优缺点
优点:
扩展一个对象的功能比继承更加灵活,不会导致类的个数急剧增加;可以通过动态的方式来扩展一个对象的功能,可以在运行时选择具体的装饰类;
可以对一个对象多次装饰,可以有多种组合;
具体的构件类与具体的装饰类可以独立变化,依据需求来增加模块而不修改之前的;缺点:
类数量增多,我们整了一些抽象构件,具体抽象对象,抽象装饰类,具体装饰类;对于多次装饰的时候,排错会更加困难;
装饰模式和代理模式对比
代理模式中,代理类对被代理的对象有控制权,决定其执行或者不执行。而装饰模式中,装饰类对代理对象没有控制权,只能为其增加一层装饰,以加强被装饰对象的功能,仅此而已