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. 关于Apple设备私有的apple-touch-icon属性详解

    以前我们用过favicon在浏览器给网站进行身份标识,用法如下: <link href="http://image.feeliu.com/web/favicon.ico" r ...

  2. centos7 玩aapt 安卓应用apk解包工具的安装

    最近在做一个应用市场的项目,需要在centos7下面对apk解包读取其信息,这就想到了使用Google的解包工具aapt,但是由于中国的原因,国内访问原生工具的地址就有些麻烦,这里就贴出地址:http ...

  3. 使用Express搭建服务器

    Express是基于Node.js平台,快速.开放.极简的web开发框架.所以,使用Express之前,请确保已安装Node.js. 1.创建一个目录作为当前工作目录: $ mkdir myapp $ ...

  4. 获取Ip 的地域等信息接口-实例

    今天项目要用到 查询访问网站用户的IP 然后网上查询了 资料还很多 不过有些已经失效了 在这总结下 腾讯,pconline 的API已经失效 不能使用 淘宝的IP接口地址: http://ip.tao ...

  5. ubuntu14.04安装wine以及国际版QQ

    公社(http://www.linuxidc.com/linux/2014-06/103768.htm)说: Wine (“Wine Is Not an Emulator” 的首字母缩写)是一个能够在 ...

  6. mybaits不能出现小于号

    org.xml.sax.SAXParseException; lineNumber: 146; columnNumber: 54; The content of elements must consi ...

  7. Junit4拓展工具JCategory与Testng的Group功能比较

    前言 笔者前段时间分享过一遍文章,关于如何通过引入新注解来扩展Junit4,以解决Process上的问题: JUnit扩展:引入新注解Annotation 最近在跟外面的同事聊的时候,得知Testng ...

  8. BZOJ 3677 连珠线

    Description 在达芬奇时代,有一个流行的儿童游戏称为连珠线.当然,这个游戏是关于珠子和线的.线是红色或蓝色的,珠子被编号为\(1\)到\(n\).这个游戏从一个珠子开始,每次会用如下方式添加 ...

  9. 当我们在谈论 DevOps,我们在谈论什么?

    Cloud Insight 携手 BearyChat:打造适合运维人员的团队协作工具 走过 C 轮的 OneAPM,旗下的产品已经日渐丰满,从应用性能监控的 Application Insight 到 ...

  10. UVA 586 Instant Complexity

    给出一段程序,求运行时间. 现在只考虑一层LOOP,不妨用数组a[i]来表示n的i次方的系数.如果输入OP m,那么就在a[0]上加m,遇到END,就说明循环结束了,需要在系数上乘以循环次数.如果次数 ...