java/android 设计模式学习笔记(7)---装饰者模式

  这篇将会介绍装饰者模式(Decorator Pattern),装饰者模式也称为包装模式(Wrapper Pattern),结构型模式之一,其使用一种对客户端透明的方式来动态的扩展对象的功能,同时它也是继承关系的一种替代方案之一,但比继承更加灵活。在现实生活中也可以看到很多装饰者模式的例子,或者可以大胆的说装饰者模式无处不在,就拿一件东西来说,可以给它披上无数层不一样的外壳,但是这件东西还是这件东西,外壳不过是用来扩展这个东西的功能而已,这就是装饰者模式,装饰者的这个角色也许各不相同但是被装饰的对象本质是不变的。

  我们的目标是允许类统一扩展,在不修改现有代码的情况下,就可搭配新的行为。如能实现这样的目标,有什么好处呢?这样的设计具有弹性,可以应对改变,可以接受新的功能来应对改变的需求,也就是 OO 原则中的对扩展开放和对修改关闭的开闭原则。

  转载请注明出处:http://blog.csdn.net/self_study/article/details/51591709

  PS:对技术感兴趣的同鞋加群544645972一起交流

设计模式总目录

  java/android 设计模式学习笔记目录

特点

  动态地给一个对象添加一些额外的职责,就增加功能来说,装饰者模式相比生成子类更加灵活,提供了有别于继承的另一种选择。

  装饰者模式可以静态的,或者根据需要可以动态的在运行时为一个对象扩展功能。被装饰者和众多的装饰者都是继承自一个接口,他们有着一样的行为特性。装饰者模式是继承的另一种选择方式,继承是在编译的时候为类添加新的行为,并且这个改变会影响所有原来该类的实体,装饰者模式就不一样,它提供一种能够在运行时根据需要选择不同运行对象的功能。装饰者模式和继承这两种方式的不同之处在某些扩展功能的情况下显得尤为重要,在一些面向对象编程的语言中,类无法在运行时被创建,而且当需要扩张功能时,这些行为往往无法预测,这就意味着在每个可能的情况下,这个类都需要被创建,所以对比之下,装饰者模式优点在于每个装饰者都是对象,在运行时被创建,并且能够在每次使用时根据需要自己组合。

UML类图

  我们现在来看看装饰者模式的 uml 类图:

  

装饰者模式共有四大角色:

  • Component:抽象组件
  • 可以是一个接口或者是抽象类,其充当的就是被装饰的原始对象,用来定义装饰者和被装饰者的基本行为。

  • ConcreteComponent:组件具体实现类
  • 该类是 Component 类的基本实现,也是我们装饰的具体对象。

  • Decorator:抽象装饰者
  • 装饰组件对象,其内部一定要有一个指向组件对象的引用。在大多数情况下,该类为抽象类,需要根据不同的装饰逻辑实现不同的具体子类。当然,如果是装饰逻辑单一,只有一个的情况下我们可以忽略该类直接作为具体的装饰者。

  • ConcreteDecoratorA 和 ConcreteDecoratorB:装饰者具体实现类
  • 对抽象装饰者的具体实现。

  在已有的 Component 和 ConcreteComponent 体系下,实现装饰者模式来扩展原有系统的功能,可以分为 5 个步骤

  1. 继承或者实现 Component 组件,生成一个 Decorator 装饰者抽象类;
  2. 在生成的这个 Decorator 装饰者类中,增加一个 Component 的私有成员对象;
  3. 将 ConcreteComponent 或者其他需要被装饰的对象传入 Decorator 类中并赋值给上一步的 Component 对象;
  4. 在装饰者 Decorator 类中,将所有的操作都替换成该 Component 对象的对应操作;
  5. 在 ConcreteDecorator 类中,根据需要对应覆盖需要重写的方法。

  装饰者模式在源码中用的也是非常多的,在 Java 和 Android 中都能够见到装饰者模式的影子:

Java 中的装饰者模式

  最典型的就是 Java 中的 java.io 包下面的 InputStream 和 OutputStream 相关类了,初学 Java 的时候,看到这些类,头都大了,其实学了装饰者模式之后,再理解这些类就很简单了,画一个简单的类图来表示:

InputStream 类是一个抽象组件, FileInputStream,StringBufferInputStream 和 ByteArrayInputStream 类都是可以被装饰者包起来的具体组件;FilterInputStream 是一个抽象装饰者,所以它的四个子类都是一个个装饰者了。

Android 中的装饰者模式

  其次,对于 android 开发工程师来说,最最重要的就应该是“上帝类” Context 和其子类了,这些类我就不用解释了,上一张类图基本就明确了:

  

所以对于 Application,Activity 和 Service 等类来说,他们只是一个个装饰者,都是用来装饰 ContextImpl 这个被装饰者类,Application 是在 createBaseContextForActivity 方法中,通过 ContextImpl 的静态方法 createActivityContext 获得一个 ContextImpl 的实例对象,并通过 setOuterContext 方法将两者建立关联;Activity 是通过 handleLaunchActivity 方法设置的 ContextImpl 实例,这个方法会获取到一个Activity对象,在performLaunchActivity函数中会调用该activity的attach方法,这个方法把一个ContextImpl对象attach到了Activity中,具体可以看看我的这篇博客,里面详细介绍到了 Activity 的启动过程:android 不能在子线程中更新ui的讨论和分析。别的类在这里就不介绍了,具体的大家可以去网上查阅相关资料。

示例与源码

  我们这里以一个图形系统中的 Window 为例,一般情况窗口都是能够垂直或者是左右欢动的,所以为了能够更好的支持 Window 的滑动,给滑动的 Window 加上一个 ScrollBar 是一个不错的方法,为了重用代码,水平滑动的 Window 和垂直滑动的 Window 我们就能够使用装饰者模式去处理,基本类图如下所示:

  

根据类图,我们首先实现 Window 这个接口:

IWindow.class

public interface IWindow {

    void draw();

    String getDescription();
}

然后是被装饰者 SimpleWindow 类,它实现了窗口的基本行为:

SimpleWindow.class

public class SimpleWindow implements IWindow {
    @Override
    public void draw() {
        Log.e("shawn", "drawing a window");
    }

    @Override
    public String getDescription() {
        return "a window";
    }
}

然后是装饰者类角色的抽象父类:

WindowDecorator.class

public abstract class WindowDecorator implements IWindow{

    private IWindow window;

    public WindowDecorator(IWindow window) {
        this.window = window;
    }

    @Override
    public void draw() {
        window.draw();
    }

    @Override
    public String getDescription() {
        return window.getDescription();
    }
}

最后是实现该装饰者父类的装饰者子类:

HorizontalScrollBarDecorator.class

public class HorizontalScrollBarDecorator extends WindowDecorator {

    public HorizontalScrollBarDecorator(IWindow window) {
        super(window);
    }

    @Override
    public void draw() {
        super.draw();
        Log.e("shawn", "then drawing the horizontal scroll bar");
    }

    @Override
    public String getDescription() {
        return super.getDescription() + " with horizontal scroll bar";
    }
}

VerticalScrollBarDecorator.class

public class VerticalScrollBarDecorator extends WindowDecorator {

    public VerticalScrollBarDecorator(IWindow window) {
        super(window);
    }

    @Override
    public void draw() {
        super.draw();
        Log.e("shawn", "then drawing the vertical scroll bar");
    }

    @Override
    public String getDescription() {
        return super.getDescription() + " with vertical scroll bar";
    }
}

最后测试代码:

switch (v.getId()) {
    case R.id.btn_horizontal_window:
        IWindow horizontalWindow = new HorizontalScrollBarDecorator(new SimpleWindow());
        horizontalWindow.draw();
        Log.e("shawn", "window description : " + horizontalWindow.getDescription());
        break;
    case R.id.btn_vertical_window:
        IWindow verticalWindow = new VerticalScrollBarDecorator(new SimpleWindow());
        verticalWindow.draw();
        Log.e("shawn", "window description : " + verticalWindow.getDescription());
        break;
}

结果:

com.android.decoratorpattern E/shawn: drawing a window
com.android.decoratorpattern E/shawn: then drawing the horizontal scroll bar
com.android.decoratorpattern E/shawn: window description : a window with horizontal scroll bar
com.android.decoratorpattern E/shawn: drawing a window
com.android.decoratorpattern E/shawn: then drawing the vertical scroll bar
com.android.decoratorpattern E/shawn: window description : a window with vertical scroll bar

代码一目了然,结构清晰。

  其实说到底,每一个写过 Android 程序的人都应该用过装饰者模式,因为每写一个 Activity,就相当于是写了一个装饰者类,不经意间就用了装饰者模式,大家想一想是不是,哈哈~~

总结

  装饰者模式和代理模式有点类似,很多时候需要仔细辨别,容易混淆,倒不是说会把代理模式看成装饰者模式,而是会把装饰者模式看作代理模式。区分一下,装饰者模式的目的是透明地为客户端对象扩展功能,是继承关系的一种替代方案,而代理模式则是给一个对象提供一个代理对象,并由代理对象来控制对原有对象的引用。装饰者模式应该为所装饰的对象增强功能;代理模式对代理的对象施加控制,但不对对象本身的功能进行增强。

  同时有几个要点需要提一下:

  • 继承属于扩展形式之一,但不一定是达到弹性设计的最佳方案;
  • 在我们的设计,应该尽量对修改关闭,对扩展开发,无需修改现有代码;
  • 组合和委托可用于在运行时动态加上新的行为;
  • 装饰者可以在被装饰者行为的前后根据实际情况加上自己的行为,必要时也可以将被装饰者行为给替换掉;
  • 可以用无数个装饰者包装一个组件,也就是说,装饰者 A 包装了被装饰者 B ,装饰者 C 再包装装饰者 A,根据实际情况这种行为可以累加到多层,通俗讲就是套上多层外壳;
  • 同时,被装饰者也可以存在多个,也就是说 ConcreteComponent 这个角色也可以是多个的。

  装饰者模式的优点就是它的特点:可以在运行时动态,透明的为一个组件扩展功能,比继承更加灵活;缺点也很明显:它会导致设计中出现许多小对象,如果过度使用,会让程序变得很复杂。

源码下载

  https://github.com/zhaozepeng/Design-Patterns/tree/master/DecoratorPattern

引用

  https://en.wikipedia.org/wiki/Decorator_pattern

  http://blog.csdn.net/jason0539/article/details/22713711

时间: 2024-08-01 10:44:35

java/android 设计模式学习笔记(7)---装饰者模式的相关文章

java/android 设计模式学习笔记(14)---外观模式

这篇博客来介绍外观模式(Facade Pattern),外观模式也称为门面模式,它在开发过程中运用频率非常高,尤其是第三方 SDK 基本很大概率都会使用外观模式.通过一个外观类使得整个子系统只有一个统一的高层的接口,这样能够降低用户的使用成本,也对用户屏蔽了很多实现细节.当然,在我们的开发过程中,外观模式也是我们封装 API 的常用手段,例如网络模块.ImageLoader 模块等.其实我们在开发过程中可能已经使用过很多次外观模式,只是没有从理论层面去了解它. 转载请注明出处:http://bl

java/android 设计模式学习笔记(10)---建造者模式

这篇博客我们来介绍一下建造者模式(Builder Pattern),建造者模式又被称为生成器模式,是创造性模式之一,与工厂方法模式和抽象工厂模式不同,后两者的目的是为了实现多态性,而 Builder 模式的目的则是为了将对象的构建与展示分离.Builder 模式是一步一步创建一个复杂对象的创建型模式,它允许用户在不知道内部构建细节的情况下,可以更精细地控制对象的构造流程.一个复杂的对象有大量的组成部分,比如汽车它有车轮.方向盘.发动机.以及各种各样的小零件,要将这些部件装配成一辆汽车,这个装配过

java/android 设计模式学习笔记(9)---代理模式

这篇博客我们来介绍一下代理模式(Proxy Pattern),代理模式也成为委托模式,是一个非常重要的设计模式,不少设计模式也都会有代理模式的影子.代理在我们日常生活中也很常见,比如上网时连接的代理服务器地址,更比如我们平时租房子,将找房子的过程代理给中介等等,都是代理模式在日常生活中的使用例子. 代理模式中的代理对象能够连接任何事物:一个网络连接,一个占用很多内存的大对象,一个文件,或者是一些复制起来代价很高甚至根本不可能复制的一些资源.总之,代理是一个由客户端调用去访问幕后真正服务的包装对象

java/android 设计模式学习笔记(一)---单例模式

前段时间公司一些同事在讨论单例模式(我是最渣的一个,都插不上嘴 T__T ),这个模式使用的频率很高,也可能是很多人最熟悉的设计模式,当然单例模式也算是最简单的设计模式之一吧,简单归简单,但是在实际使用的时候也会有一些坑. PS:对技术感兴趣的同鞋加群544645972一起交流 设计模式总目录 java/android 设计模式学习笔记目录 特点 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 单例模式的使用很广泛,比如:线程池(threadpool).缓存(cache).对

java/android 设计模式学习笔记(13)---享元模式

这篇我们来介绍一下享元模式(Flyweight Pattern),Flyweight 代表轻量级的意思,享元模式是对象池的一种实现.享元模式用来尽可能减少内存使用量,它适合用于可能存在大量重复对象的场景,缓存可共享的对象,来达到对象共享和避免创建过多对象的效果,这样一来就可以提升性能,避免内存移除和频繁 GC 等. 享元模式的一个经典使用案例是文本系统中图形显示所用的数据结构,一个文本系统能够显示的字符种类就是那么几十上百个,那么就定义这么些基础字符对象,存储每个字符的显示外形和其他的格式化数据

java/android 设计模式学习笔记(12)---组合模式

这篇我们来介绍一下组合模式(Composite Pattern),它也称为部分整体模式(Part-Whole Pattern),结构型模式之一.组合模式比较简单,它将一组相似的对象看作一个对象处理,并根据一个树状结构来组合对象,然后提供一个统一的方法去访问相应的对象,以此忽略掉对象与对象集合之间的差别.这个最典型的例子就是数据结构中的树了,如果一个节点有子节点,那么它就是枝干节点,如果没有子节点,那么它就是叶子节点,那么怎么把枝干节点和叶子节点统一当作一种对象处理呢?这就需要用到组合模式了. 转

java/android 设计模式学习笔记(6)---适配器模式

这篇来介绍一下适配器模式(Adapter Pattern),适配器模式在开发中使用的频率也是很高的,像 ListView 和 RecyclerView 的 Adapter 等都是使用的适配器模式.在我们的实际生活中也有很多类似于适配器的例子,比如香港的插座和大陆的插座就是两种格式的,为了能够成功适配,一般会在中间加上一个电源适配器,形如: 这样就能够将原来不符合的现有系统和目标系统通过适配器成功连接. 说到底,适配器模式是将原来不兼容的两个类融合在一起,它有点类似于粘合剂,将不同的东西通过一种转

java/android 设计模式学习笔记(16)---命令模式

这篇博客我们来介绍一下命令模式(Command Pattern),它是行为型设计模式之一.命令模式相对于其他的设计模式更为灵活多变,我们接触比较多的命令模式个例无非就是程序菜单命令,如在操作系统中,我们点击关机命令,系统就会执行一系列的操作,如先是暂停处理事件,保存系统的一些配置,然后结束程序进程,最后调用内核命令关闭计算机等,对于这一系列的命令,用户不用去管,用户只需点击系统的关机按钮即可完成如上一系列的命令.而我们的命令模式其实也与之相同,将一系列的方法调用封装,用户只需调用一个方法执行,那

java/android 设计模式学习笔记(3)---工厂方法模式

这篇来介绍一下工厂方法模式(Factory Method Pattern),在实际开发过程中我们都习惯于直接使用 new 关键字用来创建一个对象,可是有时候对象的创造需要一系列的步骤:你可能需要计算或取得对象的初始设置:选择生成哪个子对象实例:或在生成你需要的对象之前必须先生成一些辅助功能的对象,这个时候就需要了解该对象创建的细节,也就是说使用的地方与该对象的实现耦合在了一起,不利于扩展,为了解决这个问题就需要用到我们的工厂方法模式,它适合那些创建复杂的对象的场景,工厂方法模式也是一个使用频率很