装饰模式Decorator

定义

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

原则

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值