1、模式简介

  装饰者模式允许向一个现有的对象添加新的功能,同时又不改变其结构。

  装饰者模式的思路是用“调料”对象将原始对象进行层层包裹,同时其属性、动作层层传递,达到最终的扩展效果。因此,要求原始对象和“调料”对象继承同一个类或实现同一个接口,这样才能在装饰完成后调用原始对象的属性或动作。

装饰者模式的优点:

  • 可以动态的扩展功能;
  • 装饰者和被装饰者解耦,互相不关联。

装饰者模式的缺点:

  多层装饰比较复杂。

装饰者模式的适用场景:

  • 扩展一个类的功能;
  • 动态增加和撤销功能。

注意:装饰者模式可以替代繁杂的继承,但其内部实现使用的也是继承。也就是说,装饰者模式将繁杂的继承转化成了其内部的简单的继承。

2、案例

2.1、需求

  一个咖啡店中提供四种咖啡:综合咖啡(HouseBlend)、深焙咖啡(Dark Roast)、低咖啡因咖啡(Espresso)和浓缩咖啡(Decaf)。

  消费者除了可以买这些已经调配好的咖啡之外,还可以添加自己喜欢的调料。供选择的调料有:牛奶(Milk)、巧克力摩卡(Mocha)、豆浆(Soy)和奶泡(Whip)。

  每种咖啡或调料都有自己的价钱,消费者通过选择不同的咖啡和调料进行组合,最终得到最终组合的详细信息(咖啡和调料的名称)以及最终的价钱。

2.2、分析

  通过描述,我们的程序中需要有两类东西:咖啡和调料,这两种东西可以任意的搭配。针对这个问题我们有如下两种方案:

方案一:

  我们可以针对每一种可能有的选择编写一个类,如:加一份摩卡的综合咖啡、加两份豆浆和一份奶泡的低咖啡因咖啡,等等。这样做的结果是我们会面临一个非常严重的问题——类爆炸。而且,如果牛奶的价钱上调了,我们也不好维护,因此这种方案是不可行的。

方案二:

  我们可以使用设计模式中的装饰者模式,将咖啡使用调料层层包裹,最终得到消费者想要的口味搭配。这样,我们就只需要四种咖啡和四种调料这8个类就足够了。

  上面说到,如果使用装饰者模式,那么装饰者和被装饰者都需要继承自同一个父类或实现同一个接口,便于层层包裹。我们这里让这8个类都继承自一个Beverage的抽象类,同时四种调料继承一个继承自Beverage类的装饰者类CondimentDecorator。具体UML图如下所示:

2.3、实现

  Beverage抽象类:

public abstract class Beverage {
protected String description; public String getDescription() {
return description;
} public abstract double getCost();
}

  HouseBlend类:

public class HouseBlend extends Beverage {

   public HouseBlend() {
super.description = "HouseBlend Coffee";
} @Override
public double getCost() {
return 20.5;
}
}

  Milk中的代码:

public class Milk extends CondimentDecorator {
private Beverage beverage; public Milk(Beverage beverage) {
this.beverage = beverage;
} @Override
public String getDescription() {
return beverage.getDescription() + " + Milk";
} @Override
public double getCost() {
return beverage.getCost() + 5.5;
}
}

  Test类:

public class Test {
public static void main(String[] args) {
// 一杯低咖啡因咖啡
Beverage beverage1 = new Espresso();
System.out.println(beverage1.getDescription() + " = " + beverage1.getCost());
// 一杯浓缩咖啡,加两份摩卡
Beverage beverage2 = new Decaf();
beverage2 = new Mocha(beverage2);
beverage2 = new Mocha(beverage2);
System.out.println(beverage2.getDescription() + " = " + beverage2.getCost());
// 一杯综合咖啡,加一份牛奶和一份摩卡
Beverage beverage3 = new HouseBlend();
beverage3 = new Milk(beverage3);
beverage3 = new Mocha(beverage3);
System.out.println(beverage3.getDescription() + " = " + beverage3.getCost());
}
}

  运行结果如下图所示:

  最后贴出装饰者模式的GitHub代码:【GitHub - Decorator】

【设计模式 - 9】之装饰者模式(Decorator)的更多相关文章

  1. 设计模式学习心得<装饰器模式 Decorator>

    装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰类,用来包装 ...

  2. 设计模式入门之装饰器模式Decorator

    //装饰模式定义:动态地给一个对象加入一些额外的职责. //就添加功能来说.装饰模式比生成子类更为灵活 //这也提现了面向对象设计中的一条基本原则,即:尽量使用对象组合,而不是对象继承 //Compo ...

  3. 浅谈设计模式--装饰者模式(Decorator Pattern)

    挖了设计模式这个坑,得继续填上.继续设计模式之路.这次讨论的模式,是 装饰者模式(Decorator Pattern) 装饰者模式,有时也叫包装者(Wrapper),主要用于静态或动态地为一个特定的对 ...

  4. 设计模式(八)装饰器模式Decorator(结构型)

    设计模式(八)装饰器模式Decorator(结构型) 1. 概述 若你从事过面向对象开发,实现给一个类或对象增加行为,使用继承机制,这是所有面向对象语言的一个基本特性.如果已经存在的一个类缺少某些方法 ...

  5. 设计模式学习--装饰者模式(Decorator Pattern)

    概念: 装饰者模式(Decorator Pattern): 动态地将功能添加到对象,相比生成子类更灵活,更富有弹性. 解决方案: 装饰者模式的重点是对象的类型,装饰者对象必须有着相同的接口,也也就是有 ...

  6. 【PHP设计模式 09_ZhuangShiQi.php】装饰器模式 (decorator)

    <?php /** * [装饰器模式 (decorator)] * 有时候发布一篇文章需要经过很多人手,层层处理 */ header("Content-type: text/html; ...

  7. 设计模式 - 装饰者模式(Decorator Pattern) Java的IO类 用法

    装饰者模式(Decorator Pattern) Java的IO类 用法 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26716 ...

  8. 设计模式 - 装饰者模式(Decorator Pattern) 具体解释

    装饰者模式(Decorator Pattern) 具体解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26707033 装饰者 ...

  9. 大话设计模式--装饰者模式 Decorator -- C++实现实例

    1.装饰者模式 Decorator 动态地给一个对象添加一个额外的职责, 就添加功能来说, 装饰模式比生成子类更为灵活. 每个装饰对象的实现和如何使用这个对象分离,  每个装饰对象只关心自己的功能,不 ...

  10. JAVA设计模式之【装饰者模式】

    JAVA设计模式之[装饰者模式] 装饰模式 对新房进行装修并没有改变房屋的本质,但它可以让房子变得更漂亮.更温馨.更实用. 在软件设计中,对已有对象(新房)的功能进行扩展(装修). 把通用功能封装在装 ...

随机推荐

  1. Centos 6 设置静态 IP 地址

    编辑文件进行IP设置: [root@test1 ~]# vi /etc/sysconfig/network-scripts/ifcfg-eth0 原文件内容前面加#被注释掉 下面为新加的内容 #DEV ...

  2. 浅谈ListBox控件,将对象封装在listBox中,在ListBox中显示对象中某个属性,在ListBox中移除和移动信息

    大家好,俗称万事开头难,不经历风雨,怎能见彩虹.在此小编给大家带来一个自己练习的小实例,希望与大家一起分享与交流.下面进入应用场景,从SQL2008数据库取出数据,给ListBox赋值在界面并显示出来 ...

  3. Unity NGUI和UGUI与模型、特效的层级关系

    目录 1.介绍两大UI插件NGUI和UGUI 2.unity渲染顺序控制方式 3.NGUI的控制 4.UGUI的控制 5.模型深度的控制 6.粒子特效深度控制 7.NGUI与模型和粒子特效穿插层级管理 ...

  4. Linux 防火墙设置,禁止某个ip访问

    service  iptables  status        查看防火墙状态 service  iptables  start           开启防火墙 service  iptables  ...

  5. max os 安装python模块PIL

    下载libjpeg和zlib: http://www.ijg.org/files/jpegsrc.v9.tar.gz http://zlib.net/zlib-1.2.8.tar.gz 安装libjp ...

  6. C# Json反序列化处理

    最近换工作了 从客户端转到Web端 第一个任务就是去别人的页面上抓取数据 用到的是JSON 因为他们网站json的格式有点怪 所以 就在JSON反序列化上面 花了一点时间 首先用到的工具是http:/ ...

  7. 【转】转移Package Cache文件夹,转移Windows Installer文件夹

    详见http://blogs.msdn.com/b/heaths/archive/2014/02/11/how-to-relocate-the-package-cache.aspx (注意:若Wind ...

  8. 自己挖坑自己跳 之JsonMappingException: (was java.lang.NullPointerException) (through reference chain:)

    在Web项目中,我们经常会设计一些与界面相对应的JavaBean作为Entity,而为了兼容前台传入的空值,有些字段我们会用包装类型而不是基本类型.可是往往我的Entity已经设计完成,很多时候我们会 ...

  9. Phonegap 3.0 设置APP是否全屏

    Phonegap 3.0 默认是全屏,如需要取消全屏,可手动修改config, 在APP/res/xml/config.xml文件可设置preference: <?xml version='1. ...

  10. RedHat 6.5 离线安装 apache2.4.23

    第一部分:安装gcc等 rpm -ivh mpfr-2.4.1-6.el6.x86_64.rpm rpm -ivh ppl-0.10.2-11.el6.x86_64.rpm rpm -ivh cpp- ...