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

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

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

  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. Ubuntu 软件包管理详解

    原文转载自:http://www.cppblog.com/jb8164/archive/2009/01/09/71583.html Ubuntu 方便宜用,最值得让人称道的便是其安装软件的方式, 一条 ...

  2. list、tuple、dict、set、map

    list Python内置的一种数据类型是列表. list是一种有序的集合,可以随时添加和删除其中的元素. # 创建list classmate = ['micheal', 'Bob', 'Tracy ...

  3. sonar 代码质量管理平台

    1) 下载    从sonar官网http://www.sonarsource.org/下载 (版本当然是最新的了)   在官网上是不分系统的,一个zip包,下下来之后,包里包含 windows .l ...

  4. PhoneGap搭建运行环境(3.2版本)

    一. 1.准备环境nodejs(http://nodejs.org/download/) 2.ant(http://ant.apache.org/bindownload.cgi) 3.Android ...

  5. Spark SQL概念学习系列之Spark SQL的简介(一)

    Spark SQL提供在大数据上的SQL查询功能,类似于Shark在整个生态系统的角色,它们可以统称为SQL on Spark. 之前,Shark的查询编译和优化器依赖于Hive,使得Shark不得不 ...

  6. hello, angular

    开始系统学习一下angular!首先是hello world.根据官网给出的例子,我们一下做出下面这个东西: <!DOCTYPE html> <html ng-app> < ...

  7. HDU 5839 Special Tetrahedron (计算几何)

    Special Tetrahedron 题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5839 Description Given n points ...

  8. 咏南中间件支持DELPHI低版本开发的两层程序平稳升级到三层

    提供DELPHI中间件及中间件集群,有意请联系. N年前,我们用DELPHI低版本开发的两层程序(比如工厂ERP系统),现在仍然在企业广泛地得到使用,但老系统有些跟不上企业的发展需要了.主要表现在:虽 ...

  9. POJ 3169 Layout (spfa+差分约束)

    题目链接:http://poj.org/problem?id=3169 差分约束的解释:http://www.cnblogs.com/void/archive/2011/08/26/2153928.h ...

  10. C#枚举数值与名称的转换

    在应用枚举的时候,时常需要将枚举和数值相互转换的情况.有时候还需要转换成相应的中文.下面介绍一种方法. 首先建立一个枚举: /// <summary> /// 颜色 /// </su ...