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

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

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

  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. 用Windows Live Writer发来

    文字     package com.myeclipseide.example.myblog.secure; import com.opensymphony.xwork2.ActionSupport; ...

  2. Asmack离线消息时间获取

    DelayInformation info = (DelayInformation)message.getExtension("x","jabber:x:delay&qu ...

  3. 转】Maven学习总结(六)——Maven与Eclipse整合

    原博文出自于: http://www.cnblogs.com/xdp-gacl/p/4052025.html 感谢! 一.安装Maven插件 下载下来的maven插件如下图所示:,插件存放的路径是:E ...

  4. Hadoop应用开发实战案例 第1周

    本课程的基础课程是,Hadoop数据分析平台课程.相信,能看我本博文的朋友,是有一定的基础了. 只是前个课程是讲解,这个课程是应用. 第一层是:数据源层,代表有生产线上的数据,比如关系型数据库orca ...

  5. [iOS 多线程 & 网络 - 2.8] - 检测网络状态

    A.说明 在网络应用中,需要对用户设备的网络状态进行实时监控,有两个目的:(1)让用户了解自己的网络状态,防止一些误会(比如怪应用无能)(2)根据用户的网络状态进行智能处理,节省用户流量,提高用户体验 ...

  6. ASP.NET(C#)中的try catch异常处理机制

    在开发一个Umbraco平台系统的过程中,遇到了问题. 写的代码如下 fileUrl = MediaHelper.GetMediaUrl(Convert.ToInt32(publishedConten ...

  7. Codeforces Round #260 (Div. 1) A. Boredom (简单dp)

    题目链接:http://codeforces.com/problemset/problem/455/A 给你n个数,要是其中取一个大小为x的数,那x+1和x-1都不能取了,问你最后取完最大的和是多少. ...

  8. POJ 1860 Currency Exchange (SPFA松弛)

    题目链接:http://poj.org/problem?id=1860 题意是给你n种货币,下面m种交换的方式,拥有第s种货币V元.问你最后经过任意转换可不可能有升值.下面给你货币u和货币v,r1是u ...

  9. Python if..else

    200 ? "200px" : this.width)!important;} --> 一.介绍 1.完整形式 if <条件判断1>: <执行1> e ...

  10. (转)HTML5实战与剖析之触摸事件(touchstart、touchmove和touchend)

    HTML5中新添加了很多事件,但是由于他们的兼容问题不是很理想,应用实战性不是太强,所以在这里基本省略,咱们只分享应用广泛兼容不错的事件,日后随着兼容情况提升以后再陆续添加分享.今天为大家介绍的事件主 ...