装饰者模式(Decorator Pattern)

*利用组合(composition)和委托(delegation)可以在运行时实现继承行为的效果,动态地给对象加上新的行为。

*利用继承扩展子类的行为,是在编译时静态决定的;利用组合的做法,可以在运行时动态地扩展对象的行为。

软件设计原则:类应该对扩展开放,对修改关闭。这就是我们常说的开放关闭原则。

*开放-关闭原则使类容易扩展,在不修改代码的情况下,通过搭配实现新的行为。这样的设计可以应对改变,比如增加新功能或需求发生变更。

OO设计技巧:允许系统在不修改代码的情况下,进行功能扩展。
    装饰者模式:动态地将责任加到对象身上。如果要扩展功能,装饰者模式提供了比继承更有弹性的替代方案。
    装饰者模式中,装饰者可以在被装饰者的行为之前或之后,加上自己的行为,以实现特性的目的。

装饰者模式的几个缺点:

(1)有时在设计中加入大量的小类,变得不容易理解。
(2)有的客户端代码依赖于特定的类型(这是个比较糟糕的习惯,违反了“针对接口编程,而不是针对实现编程”的设计原则),当服务器端引入装饰者模式时,客户端就会出现状况。
(3)装饰者模式使得实例化组件的复杂度提升。
*遵循开放-关闭原则设计系统,努力使关闭的部分(不变)和开放的部分(变化)隔离开来。

问题背景:

设计一个咖啡订单系统。咖啡可以加配料,不同的配料收费不同。如果一个顾客想要摩卡(Mocha)和奶泡(Whip)深焙咖啡(DarkRoast)该怎样去计算费用呢?

类结构图

Beverage(饮料)是一个抽象类,店内所提供的饮料都必须继承此类。cost()方法是抽象的,子类必须定义自己的实现。description(叙述)的实例变量,由每个子类设置,用来描述饮料,如“超优深焙咖啡豆”。利用getDescription()方法返回此叙述。购买咖啡时,可以要求在其中加入各种调料,如:牛奶、豆浆、摩卡等。咖啡馆会根据所加入的调料收取不同的费用。如果直接用继承,会造成类爆炸。如果从基类Beverage下手,加上实例变量代表是否加上调料(牛奶、豆浆、摩卡……),看起来是不错,但是不符合我们的设计原则。如调料价钱改变会使我们更改基类代码、一旦出现新的调料,我们就需要加上新方法,并改变超类中的cost()等,所以我们使用到了装饰者模式。

先从Beverage类下手

1

2

3

4

5

6

7

8

9

public abstract class Beverage{

    publicstring description="Uknown Beverage"

    //叙述方法已经实理

    public String getDescription(){

        return description;

    }

    //价线必须在子类中实理

    public abstract double cost();

}

实现调料(Condiment)类,同时这个类也是个装饰者类

//必须让CondimentDecorator 能够取代Beverage public abstract class CondimentDecorator extends Beverage{ //所有转饰者必须重新实现getDescription public abstract String getDescription(); }基类已经创建好了,让我们来实现那些饮料(Beverage)类

//让Espresso扩展自Beverage,因为Espresso是一种饮料

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

public class Espresso extends Beverage {

    //设置饮料的描述

    public Espresso() {

        description = "Espresso";

    }

    //计算Espresso的价钱,现在不需要管调料的价钱

    public double cost() {

    return 1.99;

    }

}

public class HouseBlend extends Beverage {

    public HouseBlend() {

        description = "HouseBlend";

    }

    public double cost() {

        return .89;

    }

}

我们已经完成了抽象组件(Beverage),有了具体组件(HouseBlend),也有了抽象装饰者(CondimentDecorator)。现在,我们就来实现具体装饰者:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

//Mocha是一个装饰者,所以扩展自CondimentDecorator

public class Mocha extends CondimentDecorator {

    //用一个实例变量记录饮料,也就是被装饰者

    Beverage beverage;

    //把饮料当做构造器参数,再由构造器将此饮料记录在实例变量中

    public Mocha(Beverage beverage) {

        this.beverage = beverage;

    }

    //利用委托的做法,得到一个叙述,然后在其后加上调料的叙述

    public String getDescription() {

        return beverage.getDescription() + ",Mocha";

    }

    //首先把调用委托给被装饰者对象,然后再加上Mocha的价钱

    public double cost() {

        return .20 + beverage.cost();

    }

}

测试代码:

1

2

3

4

5

6

7

8

9

10

11

public class App {

    public static void main(String[] args) {

        //订一杯Espresso,不需要调料

        Beverage espresso = new Espresso();

        System.out.println(espresso.getDescription() + " $" + espresso.cost());

        //订一杯调料为摩卡的HouseBlend咖啡

        Beverage houseBlend = new HouseBlend();

        houseBlend = new Mocha(houseBlend);

        System.out.println(houseBlend.getDescription() + " $" + houseBlend.cost());

    }

}

输出为:

Espresso $1.99

HouseBlend,Mocha $1.09

总结

装饰者模式:动态地将责任附件到对象上。若要扩展功能,装饰者提东了比继承更有弹性的替代方案。

*装饰者和被装饰对象有相同的超类型

*你可以用一个或者多个装饰者包装一个对象。

*既然装饰者和被装饰对象有相同的超类型,所以在任何需要原始对象(被包装的)的场合,可以用装饰过的对象代替它。

*装饰者可以在所委托被装饰者的行为前与/或之后,加上自己的行为,已达到特定的目的。

*对象可以在任何时候被装饰,所以可以在运行时动态地、不限量地用你喜欢的装饰者来装饰对象

设计原则:类应该对扩展开放,对修改关闭(开放-关闭原则)。

注意:遵循开放-关闭原则,通常会引入新的抽象层次,增加代码的复杂度。你需要把注意力集中在设计中最有可能改变的地方,然后应用开放-关闭原则。在选择需要被扩展的代码部分时要小心,每个地方都采用开放-关闭原则,是一种浪费,也没必要,还会导致代码变得复杂且难以理解。

要点:

  • 继承属于扩展形式之一,但不见得是达到弹性设计的最佳方式。
  • 在我们的设计中,应该允许行为可以被扩展,而无须修改现有的代码。
  • 组合和委托可用于在运行时动态地加上新的行为。
  • 除了继承,装饰者模式也可以让我们扩展行为,
  • 装饰者模式意味着一群装饰者类,这些类用了包装具体组件。
  • 装饰者类反映出被装饰的组件类型(事实上,他们具有相同的类型,都经过接口或继承实现)。
  • 装饰者可以在被装饰者的行为前面与/或后面加上自己的行为,甚至将被装饰者的行为整个取代掉,而达到特定的目的。
  • 你可以用无数个装饰者包装一个组件。
  • 装饰者一般对组件的客户是透明的,除非客户程序依赖于组件的具体类型。
  • 装饰者会导致设计中出现许多小对象,如果过度使用,会让程序变得很复杂。

《Head First设计模式》第三章笔记 装饰者模式相关推荐

  1. 从未这么明白的设计模式(三):装饰器模式

    本文原创地址:jsbintask的博客(食用效果最佳),转载请注明出处! 同系列文章: 从未这么明白的设计模式(二):观察者模式 从未这么明白的设计模式(一):单例模式 前言 装饰器模式是为了运行时动 ...

  2. 装饰器模式与代理模式的区别_JS设计模式(三):装饰器模式、代理模式

    Do more 做的更多,比你上级给你安排的任务! 前言 在<不止代码>中提到了几个程序员典型的思维误区: 「 拜大牛为师 」「 业务代码一样很牛逼 」「 上班太忙没时间学习 」 我之前也 ...

  3. 设计模式学习笔记——装饰(Decorator)模式

    设计模式学习笔记--装饰(Decorator)模式 @(设计模式)[设计模式, 装饰模式, decorator] 设计模式学习笔记装饰Decorator模式 基本介绍 装饰案例 类图 实现代码 Dis ...

  4. 软考中级-网络工程师第三章笔记(广域通信网)

    软考中级-网络工程师第三章笔记(广域通信网) 文章目录 软考中级-网络工程师第三章笔记(广域通信网) 前言 一.广域网概念和分类 二.公共交换电话网PSTN 三.公共数据网X.25 四.帧中继网FR ...

  5. 视觉SLAM十四讲-第三章笔记

    视觉SLAM14讲-第三章笔记 3.1 旋转矩阵 欧式变换 欧式变换:改变位资,不改变形状.大小. 旋转矩阵:R(3x3).是两个坐标系基的内积. 正交阵 行列式为1 逆表示相反的旋转 平移向量:t( ...

  6. Java设计模式之结构型:装饰器模式

    一.什么是装饰器模式: 当需要对类的功能进行拓展时,一般可以使用继承,但如果需要拓展的功能种类很繁多,那势必会生成很多子类,增加系统的复杂性,并且使用继承实现功能拓展时,我们必须能够预见这些拓展功能, ...

  7. 设计模式(十):装饰者模式

    在<JavaScript设计模式>介绍中,装饰者模式跟Mixin(混入)模式相比,是另一种可行的对象子类化(Mixin模式干的事)的替代方案. 装饰者(Decorator)模式 定义: 给 ...

  8. 设计模式之禅读书笔记—行为类模式

    设计模式之禅读书笔记-行为类模式 PDF下载地址 责任链模式 命令模式 解释器模式 迭代器模式 中介者模式 备忘录模式 观察者模式 状态模式 策略模式 模板方法模式 访问者模式 责任链模式 定义:使多 ...

  9. 大聪明教你学Java设计模式 | 第七篇:装饰器模式

    前言 大聪明在写代码的过程中发现设计模式的影子是无处不在,设计模式也是软件开发人员在软件开发过程中面临的一般问题的解决方案.大聪明本着"独乐乐不如众乐乐"的宗旨与大家分享一下设计模 ...

最新文章

  1. Chrome 被曝 0day 漏洞,可让黑客获取用户数据
  2. 研发管理101军规#001 两周迭代,形成团队持续习惯
  3. AI实时特效,魔幻修图,Adobe Photoshop相机拯救PS菜鸟
  4. 【HDOJ】1058 Humble Numbers
  5. Java多线程初学者指南(10):使用Synchronized关键字同步类方法
  6. OpenCv 005---图像像素的算术操作
  7. oracle中的listener.ora和tnsnames.ora
  8. Asp.net MVC中Html.Partial, RenderPartial, Action,RenderAction 区别和用法【转发】
  9. struct output SVM
  10. BFS HDOJ 1242 Rescue
  11. 破境Angular(三)Angular构件之模块
  12. 前端开发课件 202002
  13. mysql常用的存储过程_MySQL存储过程的基本函数
  14. Eclipse字体颜色控制
  15. 指派问题匈牙利解法以及其优化
  16. iOS 性能优化(包体积优化、内存优化、流畅性优化、启动优化、耗电优化)
  17. 五险一金相关知识(转)
  18. 优雅的避免字体侵权——微软雅黑并不免费
  19. 服务器解压文件出错,四大方法解决解压文件出错问题|解压文件出错
  20. 中国流行歌手普遍缺乏科学的高音。

热门文章

  1. http协议报文体_HTTP协议扫盲(七)请求报文之 GET、POST-FORM 和 POST-FILE
  2. PJSIP UA分析(1)--PJSUA主函数
  3. ubuntu下IP、DNS配置
  4. VMWare网络设置的3中方式
  5. java jni librtmp_librtmp 编译集成
  6. pcb过孔漏铜_【企业资讯】大正瑞地:专注PCB药水二十年,品质、性能可靠
  7. 使用正则把数字前面的符号替换_正则表达式(一) 基本表达式
  8. 【转】C#进阶系列——WebApi 接口参数不再困惑:传参详解
  9. python实验题_python实验二
  10. Azkaban运行报错:我找到的几个错误问题