这篇将会介绍装饰者模式(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 {@Overridepublic void draw() {Log.e("shawn", "drawing a window");}@Overridepublic String getDescription() {return "a window";}
}

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

public abstract class WindowDecorator implements IWindow{private IWindow window;public WindowDecorator(IWindow window) {this.window = window;}@Overridepublic void draw() {window.draw();}@Overridepublic String getDescription() {return window.getDescription();}
}

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

public class HorizontalScrollBarDecorator extends WindowDecorator {public HorizontalScrollBarDecorator(IWindow window) {super(window);}@Overridepublic void draw() {super.draw();Log.e("shawn", "then drawing the horizontal scroll bar");}@Overridepublic String getDescription() {return super.getDescription() + " with horizontal scroll bar";}
}

VerticalScrollBarDecorator.class

public class VerticalScrollBarDecorator extends WindowDecorator {public VerticalScrollBarDecorator(IWindow window) {super(window);}@Overridepublic void draw() {super.draw();Log.e("shawn", "then drawing the vertical scroll bar");}@Overridepublic 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 这个角色也可以是多个的。

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

适配器 VS 装饰者 VS 桥接 VS 代理 VS 外观

  这几个都是结构型设计模式,他们有些类似,在实际使用过程中也容易搞混,我们在这就给他们做一个对比:

适配器模式

  适配器模式和其他三个设计模式一般不容易搞混,它的作用是将原来不兼容的两个类融合在一起,uml 图也和其他的差别很大。
  uml 类图:
  

装饰者模式

  装饰者模式结构上类似于代理模式,但是和代理模式的目的是不一样的,装饰者是用来动态地给一个对象添加一些额外的职责,装饰者模式为对象加上行为,而代理则是控制访问。
  uml 类图:
  

桥接模式

  桥接模式的目的是为了将抽象部分与实现部分分离,使他们都可以独立地进行变化,所以说他们两个部分是独立的,没有实现自同一个接口,这是桥接模式与代理模式,装饰者模式的区别。
  uml 类图:
  

代理模式

  代理模式为另一个对象提供代表,以便控制客户对对象的访问,管理的方式有很多种,比如远程代理和虚拟代理等,这个在上面有,这里就不说了,而装饰者模式则是为了扩展对象。
  uml 类图:
  

外观模式

  外观模式提供一个统一的接口,用来访问子系统中的一群接口。外观定义了一个高层接口,让子系统更容易使用。
  适配器模式将一个或多个类接口变成客户端所期望的一个接口,虽然大多数资料所采用的例子中适配器只适配一个类,但是你可以适配许多类来提供一个接口让客户端访问;类似的,外观模式 也可以只针对一个拥有复杂接口的类提供简化的接口,两种模式的差异,不在于他们“包装”了几个类,而是在于它们的意图。适配器模式 的意图是,“改变”接口符合客户的期望;而外观模式的意图是,提供子系统的一个简化接口。
  uml类图:
  

源码下载

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

引用

  https://en.wikipedia.org/wiki/Decorator_pattern
  http://blog.csdn.net/jason0539/article/details/22713711

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

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

    前段时间公司一些同事在讨论单例模式(我是最渣的一个,都插不上嘴 T__T ),这个模式使用的频率很高,也可能是很多人最熟悉的设计模式,当然单例模式也算是最简单的设计模式之一吧,简单归简单,但是在实际使 ...

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

    这篇来介绍一下适配器模式(Adapter Pattern),适配器模式在开发中使用的频率也是很高的,像 ListView 和 RecyclerView 的 Adapter 等都是使用的适配器模式.在我 ...

  3. java/android 设计模式学习笔记(8)---桥接模式

    这篇博客我们来介绍一下桥接模式(Bridge Pattern),它也是结构型设计模式之一.桥接,顾名思义,就是用来连接两个部分,使得两个部分可以互相通讯或者使用,桥接模式的作用就是为被分离了的抽象部分 ...

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

    前段时间公司一些同事在讨论单例模式(我是最渣的一个,都插不上嘴 T__T ),这个模式使用的频率很高,也可能是很多人最熟悉的设计模式,当然单例模式也算是最简单的设计模式之一吧,简单归简单,但是在实际使 ...

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

    这篇来介绍一下工厂方法模式(Factory Method Pattern),在实际开发过程中我们都习惯于直接使用 new 关键字用来创建一个对象,可是有时候对象的创造需要一系列的步骤:你可能需要计算或 ...

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

    其实很早就想开始总结设计模式了,无奈刚刚换完工作,工作太忙,平时周末也太懒,难得提起精神写一点,估计时间会花的很长,不过还是自己加油吧~~. 学习笔记,顾名思义,其实就是我在平时看书,工作的笔记而已, ...

  7. 设计模式学习笔记之装饰者模式

    装饰者模式     动态的将责任附加到对象上.若要扩展功能,装饰者模式提供了比继承更有弹性的替代方案. 说明: 1.装饰者和被装饰者对象有相同的超类型: 2.可以用一个或者多个装饰者包装一个对象: 3 ...

  8. 研磨设计模式学习笔记之装饰器模式

    1.装饰器模式定义:动态地给一个对象添加一些额外的功能职责,就增加功能来说,装饰器模式要比生成子类更加灵活. 2.装饰器模式实现:在装饰器模式的实现中,为了能够实现和原来使用被装饰对象的代码无缝隙结合 ...

  9. 设计模式学习笔记——享元(Flyweight)模式

    设计模式学习笔记--享元(Flyweight)模式 @(设计模式)[设计模式, 享元模式, flyweight] 设计模式学习笔记享元Flyweight模式 基本介绍 享元案例 类图 实现代码 Big ...

最新文章

  1. UNICODE_STRING 总结
  2. python操作excel-Python对Excel(*.xls)的操作
  3. 超图js版 iclient 基本开发 - 加载基本图层(以天地图示例)和基本地图控件
  4. DL:LinearNN(numpy自定义的) solve XOR problem
  5. 项目总结(二)-一些常用的工具浅谈
  6. Android-实现一个简单的自动翻译插件
  7. 【房价地图】以城市为单位,2019
  8. PyTorch Cookbook by Eric
  9. 有效预防xss_4类防御XSS的有效方法
  10. 数据库课程设计报告——员工工资管理系统
  11. matlab打靶法求解薛定谔方程,用MATLAB语言解氢原子与类氢离子的定态薛定谔方程...
  12. 简单的水果价格排序(价格不重复)
  13. 5-2 uniapp 打包 app 自定义开屏页
  14. 设计模式之设配器模式
  15. 关于销售订单挑库发放卡接口以及发运处理卡接口的处理方式
  16. c语言与多字节编码,什么是单字节,双字节和多字节编码
  17. 三元一次方程组例题_最新《三元一次方程组及其解法》例题与讲解
  18. 2022-2027年中国互联网+长租公寓市场规模预测及投资战略咨询报告
  19. 自己弄了个数字币量化分析软件,感兴趣的朋友自己下载
  20. 膜拜共享单车数据分析

热门文章

  1. Heroku部署vue项目失败:sh: 1: vue-cli-service: not found
  2. 2021水利水电安全员考证必备判断题库
  3. 什么是API?为什么要提供API?
  4. mqtt支持加密通讯
  5. Linux系统中的定时任务及延时任务详解
  6. SEO优化看这篇就够啦
  7. 职高对口计算机应用资料,2017年湖南省职高对口计算机应用综合试卷(三校联考)...
  8. HSQLDB下载与安装
  9. python图像对比_python对比图片
  10. 2020年高压电工复审模拟考试及高压电工实操考试视频