定义
动态地给一个对象添加额外的功能。就增加的功能来说,装饰模式相比生成子类更为灵活
原则
Classes should be open for extension, but closed for modification
类应该对扩展开放,对修改关闭。
我觉得这个思想对于面向对象开发来说是非常重要的,几乎很多的设计模式源码设计都离不开这个原则。
结构说明
Component抽象组件,是一个接口或者是抽象类,就是定义我们最核心的对象,也就是最原始的对象。(注:在装饰模式中,必然有一个最基本、最核心、最原始的接口或者抽象类充当Component抽象组件)
ConcreteComponent具体组件,是最核心、最原始、最基本的接口或抽象类的实现,我们需要装饰的就是它。
Decorator装饰角色, 一般是一个抽象类,实现接口或者抽象方法,它的属性里必然有一个private变量指向Component抽象组件。(功能多的话可以独立出个抽象类来,也可以直接ConcreteDecorator)
具体装饰角色,如上图中的ConcreteDecoratorA和ConcreteDecoratorB,我们要把我们最核心的、最原始的、最基本的东西装饰成其它东西。
优点
装饰类和被装饰类可以独立发展,而不会相互耦合。换句话说,Component类无须知道Decorator类,Decorator类是从外部来扩展Component类的功能,而Decorator也不用知道具体的组件。
装饰模式是继承关系的一个替代方案。我们看装饰类Decorator,不管装饰多少层,返回的对象还是Component,实现的还是is-a的关系。
装饰模式可以动态地扩展一个实现类的功能。
例子说明
假如汽车工厂在生产一种引擎,原先的生产方法是固定,之后想到原先生产方法上增加“材料准备”以及“引擎的测试情况”才能判断产品的可行性。
//引擎接口
public interface IEngfine {
void engine();
}
//实现类
public class EngfineImp implements IEngfine {
@Override
public void engine() {
//生产
}
}
//要在原先实现类上添加功能,则用到了包装类
public class Production implements IEngfine {
private IEngfine engfineImp;
public Production(IEngfine engfineImp){
this.engfineImp = engfineImp;
}
@Override
public void engine() {
frontMethod();
engfineImp.engine();
afterMethod();
}
private void frontMethod() {
//准备材料
}
private void afterMethod() {
//测试使用
}
}
public class Test {
public static void main(String args[]){
IEngfine engfine = new EngfineImp();
//包装
IEngfine production = new Production(engfine);
production.engine();
}
}
这个例子中,IEngfine就是Component,而EngfineImp则是ConcreteComponent,Production则是包装类ConcreteDecorator。Production在它的构造方法中传入了IEngfine的实现类,进而在它的方法中去增加新的操作方法。
源码说明
典型包装类就是Context和ContextImp和ContextWrapper。我们来看下它的结构
Context就是我们的抽象组件,它提供了应用运行的基本环境,是各组件和系统服务通信的桥梁,隐藏了应用与系统服务通信的细节,简化了上层应用的开发。所以Contex就是“装饰模式”里的Component。
Context类是个抽象类,android.app.ContextImpl派生实现了它的抽象接口。ContextImpl对象会与Android框架层的各个服务(包括组件管理服务、资源管理服务、安装管理服务等)建立远程连接,通过对Android进程间的通信机制(IPC)和这些服务进行通信。所以ContextImpl就是“装饰模式”里的ConcreteComponent。
如果上层应用期望改变Context接口的实现,就需要使用android.content.ContextWrapper类,它派生自Context,其中具体实现都是通过组合的方式调用ContextImpl类的实例(在ContextWrapper中的private属性mBase)来完成的。这样的设计,使得ContextImpl与ContextWrapper子类的实现可以单独变化,彼此独立。所以可以看出ContextWrapper就是“装饰模式”里的Decorator。
Android的界面组件Activity、服务组件Service以及应用基类Application都派生于ContextWrapper,它们可以通过重载来修改Context接口的实现。所以可以看出Activity、服务组件Service以及应用基类Application就是“装饰模式”里的具体装饰角色A、B、C。
注:上图可以看出界面组件基类android.app.Activity添加了界面绘制相关的实现,增加了处理界面事件的相关接口。它存放界面中各控件的对象,并与窗口管理服务建立连接,传递界面相关的事件和操作。
我们可以看下Context,找其中一两个方法来看看
public abstract class Context {
/**
* File creation mode: the default mode, where the created file can only
* be accessed by the calling application (or all applications sharing the
* same user ID).
* @see #MODE_WORLD_READABLE
* @see #MODE_WORLD_WRITEABLE
*/
public static final int MODE_PRIVATE = 0x0000;
/**
* 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(Intent intent);
@Nullable
public abstract ComponentName startService(Intent service);
/**
* Disconnect from an application service. You will no longer receive
* calls as the service is restarted, and the service is now allowed to
* stop at any time.
*
* @param conn The connection interface previously supplied to
* bindService(). This parameter must not be null.
*
* @see #bindService
*/
public abstract void unbindService(@NonNull ServiceConnection conn);
...//等等
}
可以看到Context里面提供了很多的抽象方法,包括四大组件的启动,application启动等等。我们看下它的实现类,这里就找startActivity方法,也是大家最熟悉的
class ContextImpl extends Context {
private final static String TAG = "ContextImpl";
private final static boolean DEBUG = false;
/**
* Map from package name, to preference name, to cached preferences.
*/
private static ArrayMap<String, ArrayMap<String, SharedPreferencesImpl>> sSharedPrefs;
final ActivityThread mMainThread;
final LoadedApk mPackageInfo;
...
@Override
public void startActivity(Intent intent) {
warnIfCallingFromSystemProcess();
startActivity(intent, null);
}
@Override
public void startActivity(Intent intent, Bundle options) {
warnIfCallingFromSystemProcess();
if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
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里面实现了Context的方法,在startActivity中,最终通过主线程的Instrumentation来启动Activity,再通过Binder驱动ActivityManagerService调用启动。startActivity过程很多,这里就不扩展了。
来看下ContextWrapper包装了包装了什么
public class ContextWrapper extends Context {
Context mBase;
public ContextWrapper(Context base) {
mBase = base;
}
/**
* Set the base context for this ContextWrapper. All calls will then be
* delegated to the base context. Throws
* IllegalStateException if a base context has already been set.
*
* @param base The new base context for this wrapper.
*/
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);
}
@Override
public ComponentName startService(Intent service) {
return mBase.startService(service);
}
...
}
看出它只是单纯的调用父类Context的方法mBase.startActivity(intent),并未做修改。
我们看下ContextThemeWrapper
**
* A ContextWrapper that allows you to modify the theme from what is in the
* wrapped context.
*/
public class ContextThemeWrapper extends ContextWrapper {
private int mThemeResource;
private Resources.Theme mTheme;
private LayoutInflater mInflater;
private Configuration mOverrideConfiguration;
private Resources mResources;
...
public ContextThemeWrapper(Context base, Resources.Theme theme) {
super(base);
mTheme = theme;
}
...
@Override
public void setTheme(int resid) {
if (mThemeResource != resid) {
mThemeResource = resid;
initializeTheme();
}
}
}
可以看到ContextThemeWrapper在原来ContextWrapper上,添加了Theme的设置,而Activity正是继承了ContextThemeWrapper的主题。
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory2,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks2,
Window.OnWindowDismissedCallback {
private static final String TAG = "Activity";
private static final boolean DEBUG_LIFECYCLE = false;
...
}
看到这里,你也就知道了啦,装饰模式的耦合性和扩展性了。
参考:http://www.cnblogs.com/yemeishu/archive/2012/12/30/2839489.html