装饰者模式--《Head First DesignPattern》
装饰者模式动态地将责任附加到对象杭,若要拓展功能,装设置提供了比继承更有弹性的替代方案。
星巴兹有多种咖啡,它们具有不同的价格。在购买咖啡时,也可以要求在其中加入各种调料,例如豆浆、摩卡、奶泡等等。需要根据所加入的调料收取不同的费用。
这里运用”装饰者模式“,以饮料为主体,然后在运行时以调料来“装饰”饮料,比如说顾客想要摩卡和奶泡深焙咖啡,那么
- 哪一个深焙咖啡(DarkRoast)对象
- 以摩卡对象装饰它
- 以奶泡对象装饰它
- 调用cost()方法,并依赖委托(delegate)将调料的价格加上去。




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

- ConcreteComponent是我们要动态地加上新行为的对象
- 每个Component都可以单独地使用,或者被装饰者包起来用
- 每个装饰者都包装一个组件,也就是说,装饰者有一个实例变量以保存某个Component的应用。
- Decorator是装饰者共同实现的接口。
- 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》的更多相关文章
- Java设计模式——装饰者模式
JAVA 设计模式 装饰者模式 用途 装饰者模式 (Decorator) 动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator 模式相比生成子类更为灵活. 装饰者模式是一种结构式模式 ...
- JAVA设计模式--装饰器模式
装饰器模式 装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构.这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装. 这种模式创建了一个装饰 ...
- 从源码角度理解Java设计模式——装饰者模式
一.饰器者模式介绍 装饰者模式定义:在不改变原有对象的基础上附加功能,相比生成子类更灵活. 适用场景:动态的给一个对象添加或者撤销功能. 优点:可以不改变原有对象的情况下动态扩展功能,可以使扩展的多个 ...
- 【设计模式】Java设计模式 - 装饰者模式
Java设计模式 - 装饰者模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 原创作品,更多关注我CSDN: 一个有梦有戏的人 准备将博客园.CSDN一起记录分享自 ...
- [Head First设计模式]山西面馆中的设计模式——装饰者模式
引言 在山西面馆吃鸡蛋面的时候突然想起装饰者这个模式,觉得面馆这个场景跟书中的星巴兹咖啡的场景很像,边吃边思考装饰者模式.这里也就依葫芦画瓢,换汤不换药的用装饰者模式来模拟一碗鸡蛋面是怎么出来的吧.吃 ...
- JAVA 设计模式 装饰者模式
用途 装饰者模式 (Decorator) 动态地给一个对象添加一些额外的职责.就增加功能来说,Decorator 模式相比生成子类更为灵活. 装饰者模式是一种结构式模式. 结构
- 浅谈设计模式--装饰者模式(Decorator Pattern)
挖了设计模式这个坑,得继续填上.继续设计模式之路.这次讨论的模式,是 装饰者模式(Decorator Pattern) 装饰者模式,有时也叫包装者(Wrapper),主要用于静态或动态地为一个特定的对 ...
- javascript设计模式——装饰者模式
前面的话 在程序开发中,许多时候都并不希望某个类天生就非常庞大,一次性包含许多职责.那么可以使用装饰者模式.装饰者模式可以动态地给某个对象添加一些额外的职责,而不会影响从这个类中派生的其他对象.本文将 ...
- 设计模式-装饰者模式(Decorator Pattern)
本文由@呆代待殆原创,转载请注明出处. 此设计模式遵循的设计原则之一:类应该支持扩展,而拒绝修改(Open-Closed Principle) 装饰者模式简述 装饰者模式通过组合的方式扩展对象的特性, ...
- C#设计模式--装饰器模式
0.C#设计模式-简单工厂模式 1.C#设计模式--工厂方法模式 2.C#设计模式--抽象工厂模式 3.C#设计模式--单例模式 4.C#设计模式--建造者模式 5.C#设计模式--原型模式 6.C# ...
随机推荐
- Codeforces Round #363 (Div. 1) C. LRU
题意: n个数,长度为k的缓存,每次询问,每个数以pi的概率被选,如果不在缓存区则加入,如果缓存区满了,则第一个进缓存的出来,问10^100次询问以后每个数在缓存的概率 思路: 状压DP,看了hzwe ...
- Python的列表排序
Python的列表排序 本文为转载,源地址为:http://blog.csdn.net/horin153/article/details/7076321 在 Python 中, 当需要对一个 list ...
- Ubuntu中、英文环境设置
改变ubuntu的中英文显示需要修改文件/etc/default/locale,具体设置过程为: 1.打开/etc/default/locale文件 #sudo vim /etc/default/lo ...
- 依赖包bcrypt安装Issues
说明:本文在个人博客地址为edwardesire.com,欢迎前来品尝. 在决策树项目中,使用到了bcrypt依赖包来加密文件.在wini8(win7)部署安装这个依赖的时候容易出现出现了问题. 解决 ...
- JEE , EJB概念深入概括
说起EJB,不得不提JEE,java EE 英文全称为:java Enterprise Edition企业级应用的软件架构,是一种思想,也是一种规范,方便从事这方面的开发者以及开发厂商进行规范性的开发 ...
- cocos2d-x图片变灰或者变亮
//根据现有CCSprite,变亮和变灰 CCSprite* FlyLeaf::graylightWithCCSprite(CCSprite* oldSprite,bool isLight) { ...
- Linux数据流重定向
一.什么是数据流重导向: 数据流重导向 (redirect) 由字面上的意思来看,好像就是将『数据传导到其他地方去』,没错-数据流重导向就是将某个命令运行后应该要出现在屏幕上的数据, 给他传输到其他的 ...
- 分享iOS最喜欢的技巧和提示
转自:http://blog.csdn.net/biggercoffee/article/details/50394027 Objective-C 1.让Xcode的控制台支持LLDB类型的打印 这有 ...
- Storm ui 展示字段说明
1.Storm ui 首页 主要分为4块: Cluster Summary,Topology summary,Supervisor summary,Nimbus Configuration,如下图所示 ...
- POJ 3026 Borg Maze (最小生成树)
Borg Maze 题目链接: http://acm.hust.edu.cn/vjudge/contest/124434#problem/I Description The Borg is an im ...