【设计模式 - 9】之装饰者模式(Decorator)
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)的更多相关文章
- 设计模式学习心得<装饰器模式 Decorator>
		
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰类,用来包装 ...
 - 设计模式入门之装饰器模式Decorator
		
//装饰模式定义:动态地给一个对象加入一些额外的职责. //就添加功能来说.装饰模式比生成子类更为灵活 //这也提现了面向对象设计中的一条基本原则,即:尽量使用对象组合,而不是对象继承 //Compo ...
 - 浅谈设计模式--装饰者模式(Decorator Pattern)
		
挖了设计模式这个坑,得继续填上.继续设计模式之路.这次讨论的模式,是 装饰者模式(Decorator Pattern) 装饰者模式,有时也叫包装者(Wrapper),主要用于静态或动态地为一个特定的对 ...
 - 设计模式(八)装饰器模式Decorator(结构型)
		
设计模式(八)装饰器模式Decorator(结构型) 1. 概述 若你从事过面向对象开发,实现给一个类或对象增加行为,使用继承机制,这是所有面向对象语言的一个基本特性.如果已经存在的一个类缺少某些方法 ...
 - 设计模式学习--装饰者模式(Decorator Pattern)
		
概念: 装饰者模式(Decorator Pattern): 动态地将功能添加到对象,相比生成子类更灵活,更富有弹性. 解决方案: 装饰者模式的重点是对象的类型,装饰者对象必须有着相同的接口,也也就是有 ...
 - 【PHP设计模式 09_ZhuangShiQi.php】装饰器模式 (decorator)
		
<?php /** * [装饰器模式 (decorator)] * 有时候发布一篇文章需要经过很多人手,层层处理 */ header("Content-type: text/html; ...
 - 设计模式 - 装饰者模式(Decorator Pattern) Java的IO类 用法
		
装饰者模式(Decorator Pattern) Java的IO类 用法 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26716 ...
 - 设计模式 - 装饰者模式(Decorator Pattern) 具体解释
		
装饰者模式(Decorator Pattern) 具体解释 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26707033 装饰者 ...
 - 大话设计模式--装饰者模式 Decorator -- C++实现实例
		
1.装饰者模式 Decorator 动态地给一个对象添加一个额外的职责, 就添加功能来说, 装饰模式比生成子类更为灵活. 每个装饰对象的实现和如何使用这个对象分离, 每个装饰对象只关心自己的功能,不 ...
 - JAVA设计模式之【装饰者模式】
		
JAVA设计模式之[装饰者模式] 装饰模式 对新房进行装修并没有改变房屋的本质,但它可以让房子变得更漂亮.更温馨.更实用. 在软件设计中,对已有对象(新房)的功能进行扩展(装修). 把通用功能封装在装 ...
 
随机推荐
- 虚拟化技术与"云"
			
虚拟化技术: 如网站在某一时间访问量大,平时访问量少,如果一直保持大量的服务器提供服务,显示效率好低,浪费资源,在 不增减服务器,存储设备,网络等实际物理设备,而是利用软件将这些物理设备虚拟化,在有必 ...
 - C#.Net网页加载等待效果漂亮并且简单
			
最近网页加载数据比较多,点击后给用户就是白板很不友好,想了很久找了些资料,在网页加载中显示等待画面给客户,页面加载完成自动隐藏等待效果. 在网页后台cs代码: protected void Pa ...
 - C# 启动和结束一个线程
			
在程序执行中会遇到启动本软件的exe问,或者启用其它的exe文件,已达到执行某些操作的作用.下面是两种最常见的启动exe文件. 1.调用系统dll使用其提供的方法. 引用的dll, [DllImpor ...
 - ERROR 2003 (HY000): Can't connect to MySQL server
			
http://blog.csdn.net/longxibendi/article/details/6363934 一.问题的提出 /usr/local/webserver/mysql/bin/mysq ...
 - 如何用C程序简单演奏乐曲
			
如何用C程序简单演奏乐曲 首先我们要介绍一个函数: Beep(Frequency,time) 如果我们在程序中运行这个函数 Int main(void) { Beep(Frequency,time); ...
 - HTML5峰会小记
			
5259月228日,在大连理工的伯川图书馆举办了一次HTML5峰会,小记一下这次峰会的内容. 名为HTML5峰会,其本质就是一次各大赞助商的轮番登场,产品介绍间隙插播一下HTML5.但是并不影响这次峰 ...
 - [BZOJ 3196] 213平衡树 【线段树套set + 树状数组套线段树】
			
题目链接:BZOJ - 3196 题目分析 区间Kth和区间Rank用树状数组套线段树实现,区间前驱后继用线段树套set实现. 为了节省空间,需要离线,先离散化,这样需要的数组大小可以小一些,可以卡过 ...
 - 【Java】java数据库连接池配置的几种方法
			
今天遇到了关于数据源连接池配置的问题,发现有很多种方式可以配置,现总结如下,希望对大家有所帮助:(已Mysql数据库为例) 一,Tomcat配置数据源: 方式一:在WebRoot下面建文件夹META- ...
 - touchend与click顺序
			
http://www.w3cmm.com/javascript/touch.html click 在 touchend之前 去除超链接的虚线框 http://www.cnblogs.com/kingw ...
 - Django Sqlite3 数据库向MySQL迁移
			
整合了两个URL而来.. 1,http://www.phodal.com/blog/django-mezzanine-sqlite3-migrate-mysql/ 2,http://www.ziqia ...