装饰者模式动态地将责任附加到对象杭,若要拓展功能,装设置提供了比继承更有弹性的替代方案。

星巴兹有多种咖啡,它们具有不同的价格。在购买咖啡时,也可以要求在其中加入各种调料,例如豆浆、摩卡、奶泡等等。需要根据所加入的调料收取不同的费用。

这里运用”装饰者模式“,以饮料为主体,然后在运行时以调料来“装饰”饮料,比如说顾客想要摩卡和奶泡深焙咖啡,那么

  1. 哪一个深焙咖啡(DarkRoast)对象
  2. 以摩卡对象装饰它
  3. 以奶泡对象装饰它
  4. 调用cost()方法,并依赖委托(delegate)将调料的价格加上去。

看一下装饰者模式的类图是如何实现的:

  1. ConcreteComponent是我们要动态地加上新行为的对象
  2. 每个Component都可以单独地使用,或者被装饰者包起来用
  3. 每个装饰者都包装一个组件,也就是说,装饰者有一个实例变量以保存某个Component的应用。
  4. Decorator是装饰者共同实现的接口。
  5. ConcreteDecorator有一个实例变量,可以记录所装饰的食物。

所以实现的类图就变成:

代码如下:

Beverage,是基类,实现了getDescription(),同时cost是抽象方法。由子类去实现。

 package headfirst.decorator.starbuzz;

 public abstract class Beverage {
String description = "Unknown Beverage"; public String getDescription() {
return description;
} public abstract double cost();
}

四种咖啡:

HouseBlend,DarkRoast,Espresso,Decaf

 package headfirst.decorator.starbuzz;

 public class HouseBlend extends Beverage {
public HouseBlend() {
description = "House Blend Coffee";
} public double cost() {
return .89;
}
} public class DarkRoast extends Beverage {
public DarkRoast() {
description = "Dark Roast Coffee";
} public double cost() {
return .99;
}
} public class Espresso extends Beverage { public Espresso() {
description = "Espresso";
} public double cost() {
return 1.99;
}
} public class Decaf extends Beverage {
public Decaf() {
description = "Decaf Coffee";
} public double cost() {
return 1.05;
}
}

接下来是调料接口,或者是抽象类。它必须继承我们的Beverage。并且声明了getDescription()方法。

 package headfirst.decorator.starbuzz;

 public abstract class CondimentDecorator extends Beverage {
public abstract String getDescription();
}

接下来是四种调料:

 package headfirst.decorator.starbuzz;

 public class Mocha extends CondimentDecorator {
Beverage beverage; //保存了一个Berverage对象
public Mocha(Beverage beverage) {
this.beverage = beverage;
} public String getDescription() {
return beverage.getDescription() + ", Mocha";
} public double cost() {
//注意这里委托了beverage.cost()
return .20 + beverage.cost();
}
} public class Whip extends CondimentDecorator {
Beverage beverage; public Whip(Beverage beverage) {
this.beverage = beverage;
} public String getDescription() {
return beverage.getDescription() + ", Whip";
} public double cost() {
return .10 + beverage.cost();
}
} public class Soy extends CondimentDecorator {
Beverage beverage; public Soy(Beverage beverage) {
this.beverage = beverage;
} public String getDescription() {
return beverage.getDescription() + ", Soy";
} public double cost() {
return .15 + beverage.cost();
}
} public class Milk extends CondimentDecorator {
Beverage beverage; public Milk(Beverage beverage) {
this.beverage = beverage;
} public String getDescription() {
return beverage.getDescription() + ", Milk";
} public double cost() {
return .10 + beverage.cost();
}
}

最后是测试代码:

 package headfirst.decorator.starbuzz;

 public class StarbuzzCoffee {

     public static void main(String args[]) {
Beverage beverage = new Espresso();
System.out.println(beverage.getDescription()
+ " $" + beverage.cost()); Beverage beverage2 = new DarkRoast();
beverage2 = new Mocha(beverage2);
beverage2 = new Mocha(beverage2);
beverage2 = new Whip(beverage2);
System.out.println(beverage2.getDescription()
+ " $" + beverage2.cost()); Beverage beverage3 = new HouseBlend();
beverage3 = new Soy(beverage3);
beverage3 = new Mocha(beverage3);
beverage3 = new Whip(beverage3);
System.out.println(beverage3.getDescription()
+ " $" + beverage3.cost());
}
}

那如果我们这时产生了新的需求,要求在菜单上加上咖啡的容量的大小,供顾客选择大杯,小杯,中杯,那该怎么办?要注意,大杯的饮料比较贵,同时它加的调料也要比较多,所以调料的价格也不一样。

这时我们应该在Beverage中定义size和getSize()的函数,并且在四种饮料中要根据size的大小,cost()函数要返回不同的价格。

在调料中,我们也需要获取被装饰者的size,然后cost函数加上对应调料的价格。

 package headfirst.decorator.starbuzz;

 public class Soy extends CondimentDecorator {
Beverage beverage; public Soy(Beverage beverage) {
this.beverage = beverage;
} public String getDescription() {
return beverage.getDescription() + ", Soy";
} public int getSize(){
return beverage.getSize();
} public double cost() {
double cost = beverage.cost();
if (getSize() == Beverage.TALL){
cost += .10;
}else if (getSize() == Beverage.GRANDE){
cost += .15;
}else if (getSize() == Beverage.VENTI){
COST += .20;
}
return cost;
}
}

最后要说的就是java.io包中,定义了很多类,就是装饰者模式的实现。我们可以看看InputStream的实现。

OutputStream和Reader,Writer的设计大致差不多。

装饰者模式--《Head First DesignPattern》的更多相关文章

  1. Java设计模式——装饰者模式

    JAVA 设计模式 装饰者模式 用途 装饰者模式 (Decorator) 动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator 模式相比生成子类更为灵活. 装饰者模式是一种结构式模式 ...

  2. JAVA设计模式--装饰器模式

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

  3. 从源码角度理解Java设计模式——装饰者模式

    一.饰器者模式介绍 装饰者模式定义:在不改变原有对象的基础上附加功能,相比生成子类更灵活. 适用场景:动态的给一个对象添加或者撤销功能. 优点:可以不改变原有对象的情况下动态扩展功能,可以使扩展的多个 ...

  4. 【设计模式】Java设计模式 - 装饰者模式

    Java设计模式 - 装饰者模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 原创作品,更多关注我CSDN: 一个有梦有戏的人 准备将博客园.CSDN一起记录分享自 ...

  5. [Head First设计模式]山西面馆中的设计模式——装饰者模式

    引言 在山西面馆吃鸡蛋面的时候突然想起装饰者这个模式,觉得面馆这个场景跟书中的星巴兹咖啡的场景很像,边吃边思考装饰者模式.这里也就依葫芦画瓢,换汤不换药的用装饰者模式来模拟一碗鸡蛋面是怎么出来的吧.吃 ...

  6. JAVA 设计模式 装饰者模式

    用途 装饰者模式 (Decorator) 动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator 模式相比生成子类更为灵活. 装饰者模式是一种结构式模式. 结构

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

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

  8. javascript设计模式——装饰者模式

    前面的话 在程序开发中,许多时候都并不希望某个类天生就非常庞大,一次性包含许多职责.那么可以使用装饰者模式.装饰者模式可以动态地给某个对象添加一些额外的职责,而不会影响从这个类中派生的其他对象.本文将 ...

  9. 设计模式-装饰者模式(Decorator Pattern)

    本文由@呆代待殆原创,转载请注明出处. 此设计模式遵循的设计原则之一:类应该支持扩展,而拒绝修改(Open-Closed Principle) 装饰者模式简述 装饰者模式通过组合的方式扩展对象的特性, ...

  10. C#设计模式--装饰器模式

    0.C#设计模式-简单工厂模式 1.C#设计模式--工厂方法模式 2.C#设计模式--抽象工厂模式 3.C#设计模式--单例模式 4.C#设计模式--建造者模式 5.C#设计模式--原型模式 6.C# ...

随机推荐

  1. 2016 Multi-University Training Contest 5 1012 World is Exploding 树状数组+离线化

    http://acm.hdu.edu.cn/showproblem.php?pid=5792 1012 World is Exploding 题意:选四个数,满足a<b and A[a]< ...

  2. Tkinter教程之Toplevel篇

    本文转载自:http://blog.csdn.net/jcodeer/article/details/1811341 '''Tkinter教程之Toplevel篇'''#TopLevel与Frame类 ...

  3. MEAN stack 做网站【1】

    做一个小project,学习如何用MEAN技术栈来搭建网站. JavaScript新手,不足之处,请指出.(系统为win10) 搭建环境: 安装Node.JS (略过) 安装MySQL,MongoDB ...

  4. 最长回文子串(Longest Palindromic Substring)-DP问题

    问题描述: 给定一个字符串S,找出它的最大的回文子串,你可以假设字符串的最大长度是1000,而且存在唯一的最长回文子串 . 思路分析: 动态规划的思路:dp[i][j] 表示的是 从i 到 j 的字串 ...

  5. HDU 1142 A Walk Through the Forest (求最短路条数)

    A Walk Through the Forest 题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1142 Description Jimmy exp ...

  6. HDU 5675 ztr loves math (数学推导)

    ztr loves math 题目链接: http://acm.hust.edu.cn/vjudge/contest/123316#problem/A Description ztr loves re ...

  7. AutoCAD.NET二次开发:创建自定义菜单(AcCui)

    从CAD2007之后,Autodesk提供了一个新的程序集AcCui.dll,使用这个程序集,我们可以方便地做一些界面方面的操作,比如创建自定义菜单. 下面介绍一下菜单的创建过程: 1.在项目中添加引 ...

  8. git 入门学习笔记

    安装msysgit (模拟环境Git)安装后绑定邮箱和名字$ git config --global user.name "Your Name"$ git config --glo ...

  9. 通过OAF实现RTF多语言

    职责: Oracle XML Publisher 管理员 -->主页 -->模板 -->创建模板 1.定义rtf模板时 在创建模板时,勾选可转换.      

  10. wikioi 1214 线段覆盖

    题目描述 Description 给定x轴上的N(0<N<100)条线段,每个线段由它的二个端点a_I和b_I确定,I=1,2,--N.这些坐标都是区间(-999,999)的整数.有些线段 ...