从装饰者模式到 Context 类族

2,920 阅读6分钟

本着不学习就要落后,落后就要挨打的态度,我也开始捣鼓起了设计模式。但只看设计模式又不免有些索然无味,索性就连Android源码也一起研究研究,现在看来效果不错。昨天晚上刚看了装饰者模式,正好今天总结一番分享给大家。新手上路,如有不足之处,还请大家多指教。

Decorator模式(别名Wrapper):动态将职责附加到对象上,若要扩展功能,装饰者提供了比继承更具弹性的代替方案。

遵循的设计原则

  1. 多用组合,少用继承。

    利用继承设计子类的行为,是在编译时静态决定的,而且所有的子类都会继承到相同的行为。然而,如果能够利用组合的做法扩展对象的行为,就可以在运行时动态地进行扩展。

  2. 类应设计的对扩展开放,对修改关闭。

实现

装饰者的UML图大概如下

在装饰模式结构图中包含如下几个角色:

  • Component(抽象构件):它是具体构件和抽象装饰类的共同父类,声明了在具体构件中实现的业务方法,它的引入可以使客户端以一致的方式处理未被装饰的对象以及装饰之后的对象,实现客户端的透明操作。
  • Concrete Component(具体构件):它是抽象构件类的子类,用于定义具体的构件对象,实现了在抽象构件中声明的方法,装饰器可以给它增加额外的职责(方法)。
  • Decorator(抽象装饰类):它也是抽象构件类的子类,抽象装饰类不一定是抽象方法。用于给具体构件增加职责,但是具体职责在其子类中实现。它维护一个指向抽象构件对象的引用,通过该引用可以调用装饰之前构件对象的方法,并通过其子类扩展该方法,以达到装饰的目的。
  • Concrete Decorator(具体装饰类):它是抽象装饰类的子类,负责向构件添加新的职责。每一个具体装饰类都定义了一些新的行为,它可以调用在抽象装饰类中定义的方法,并可以增加新的方法用以扩充对象的行为。

接下来我们对应UML图实现就好。

我们先定义一个Component,可以是接口也可以是抽象类:

public interface Component
{
    void operation();
}

接下来是Concrete Component:

public class ConcreteComponent implements Component
{
    public void operation()
    {
        // Write your code here
    }
}

然后是Decorator:

public class Decorator implements Component
{
    private Component component;

    public Decorator(Component component)
    {
        this.component = component;
    }

    public void operation()
    {
        component.operation();
    }

}

最后是Concrete Decorator:

在Concrete Component的行为之前或之后,加上自己的行为,以“贴上”附加的职责。

public class ConcreteDecorator extends Decorator
{
    public void operation()
    {
        //addBehavior也可以在前面

        super.operation();

        addBehavior();
    }

    private void addBehavior()
    {
        //your code
    }
}

优点

  1. Decorator模式与继承关系的目的都是要扩展对象的功能,但是Decorator可以提供比继承更多的灵活性。

  2. 通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。

有句话叫“没吃过猪肉,还没见过猪跑么?”。我们可能没有自己在代码中应用过装饰者模式,但是我们一定见过(可能当时不认识)。比如java中的I/O库,以及Android中的Context的设计等等。接下来我们看看Context是如何组织的。

uml图

中间省略了部分类,比如ContextWrapper的实现类里边还有MutableContextWrapper、BackupAgent类等,感兴趣的朋友可以自己研究一下。

Context类

/**
* Interface to global information about an application environment. This is an abstract class whose implementation is provided by the Android system. It allows access to application-specific resources and classes, as well as up-calls for application-level operations such as launching activities,broadcasting and receiving intents, etc.
*/

注释的意思大概是这样的:这是一个由Android系统提供其实现的抽象类,它是提供应用环境(application environment)信息的接口。通过它可以访问到应用的资源和类,以及进行一些系统级别的操作,比如加载activity、发送广播和接收intent等。

粗略浏览一下里边的属性和方法,我们还可以看到一些熟悉的东西,比如getSharedPreferences(String name, int mode),以及mode参数的几种可选值;再比如getColor(@ColorRes int id)、getDrawable(@DrawableRes int id)等获取系统资源的方法等等。可以这样说,Content类是application的管家,它有权管理application的大部分资源。

这个类对应装饰者模式中的Component。

Context的实现类

ContextImpl类

/**Common implementation of Context API, which provides the base context object for Activity and other application components.
*/

ContextImpl是Context抽象类的一个直接子类,有一个私有的构造方法,是Context的具体实现类。源码位于android.app包中,但它在API文档中找不到,是一个默认访问权限的类,也就是说它只允许android.app包中的类可以调用它,或者只有和它同包的类才可以通过其父类的方法使用它。它为Activity、Service等应用组件提供基本的context对象。

这个类对应装饰者模式中的Concrete Component。

ContextWrapper

/**
 * Proxying implementation of Context that simply delegates all of its calls to
 * another Context.  Can be subclassed to modify behavior without changing
 * the original Context.
 */
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;
    }
}

注释描述的很明显:Context类的代理实现,ContextWrapper中实现Context的方法全是通过mBase来实现的。这样它(ContextWrapper)派生出的子类就可以在不改变原始context(mBase)的情况下扩展Context的行为。

这个类对应装饰者模式中的Decorator。

具体扩展

这里我们就不做过多的分析了,我们简单看下Activity的父类ContextThemeWrapper。

/**
* A context wrapper that allows you to modify or replace the theme of the wrapped context.
*/

注释中说的很清楚,这个类扩展的功能就是允许我们去修改或者替换包装的context的主题。

我们再来看看setTheme(int resid)方法。

@Override
public void setTheme(int resid) {
    if (mThemeResource != resid) {
        mThemeResource = resid;
        initializeTheme();
    }
}

这里我们没有调用父类的实现,而是自己处理了设置主题的逻辑。这样我们就用组合的方式扩展了ContextImpl中的setTeme(int resid)方法。

这个类对应装饰者模式中的Concrete Decorator。其他的Service和Application等都大同小异,只不过扩展了不同的行为。

参考链接:

Android源码在线查看

blog.csdn.net/yanbober/ar…