问题:要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便。使用面向对象来计算不同种类咖啡的费用:客户可以点单品咖啡,也可以点单品咖啡与调料的组合。
咖啡种类(单品咖啡):Espresso(意大利浓咖啡)、LongBlack(美式咖啡)、Decaf(无因咖啡)。
调料:Milk(牛奶)、Soy(豆浆)、Chocolate(巧克力)。
方案1类图:

方案1的分析:
(1)Drink是一个抽象类,表示饮料。
(2)description属性是对咖啡的描述,,比如咖啡的名字。
(3)cost() 方法就是计算费用,在Drink类中是一个抽象方法。
(4)Espresso、LongBlack、Decaf是单品咖啡, 继承Drink抽象类,并实现cost方法。
(5)EspressoAddMilk、EspressoAddMilkSoy、EspressoAddMilkSoyChocolate是单品咖啡与调料的组合,单品咖啡与调料的组合有非常多种。
方案1出现的问题:
方案1的设计会存在很多类,当增加一个单品咖啡或者调料,类的数量就会倍增,会出现类爆炸。
方案1由于咖啡单品与调料组合会造成类的倍增,因此可以做改进,将调料内置到Drink类,这样就不会造成类数量过多,从而提高项目的维护性。
方案2的类图:

方案2的分析:
方案2可以控制类的数量,不至于造成很多的类,但是在增加或者删除调料种类时,代码的维护量很大。建议使用装饰者模式。
装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp原则)。
装饰者模式就像打包一个快递(主体和包装):主体(被装饰者Component),比如陶瓷、衣服 。包装(装饰者Decorator),比如报纸、塑料泡沫、纸板、木板。
装饰者模式原理:

类图对象分析:
在Component主体与ConcreteComponent具体主体之间,如果ConcreteComponent类很多的话,还可以设计一个缓冲层,将共有的部分提取出来,抽象成一个类。


图中的一份Milk包含了LongBlack,一份Chocolate包含了 Milk+LongBlack,最后一份Chocolate包含了Chocolate+Milk+LongBlack。不管是什么形式的 单品咖啡+调料 的组合,通过递归方式可以方便的组合和维护。
package com.etc.design.decorator;
public abstract class Drink {
// 描述
public String des;
// 费用
private float price = 0.0f;
public String getDes() {
return des;
}
public void setDes(String des) {
this.des = des;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
//计算费用的抽象方法,子类来实现
public abstract float cost();
}
package com.etc.design.decorator;
public class Coffee extends Drink {
@Override
public float cost() {
return super.getPrice();
}
}
package com.etc.design.decorator;
public class LongBlack extends Coffee {
public LongBlack() {
setDes(" 美式咖啡 ");
setPrice(5.0f);
}
}
package com.etc.design.decorator;
public class Espresso extends Coffee {
public Espresso() {
setDes(" 意大利咖啡 ");
setPrice(6.0f);
}
}
package com.etc.design.decorator;
public class DeCaf extends Coffee {
public DeCaf() {
setDes(" 无因咖啡 ");
setPrice(1.0f);
}
}
package com.etc.design.decorator;
public class Decorator extends Drink {
// 组合关系
private Drink obj;
public Decorator(Drink obj) {
this.obj = obj;
}
@Override
public float cost() {
// getPrice-获取装饰者(当前类)的价格
// obj.getDes()-获取被装饰者的费用
return super.getPrice() + obj.cost();
}
@Override
public String getDes() {
// obj.getDes()-获取被装饰者的信息
return des + " " + getPrice() + " && " + obj.getDes();
}
}
package com.etc.design.decorator;
// 具体的Decorator(调味品)
public class Chocolate extends Decorator {
public Chocolate(Drink obj) {
super(obj);
setDes(" 巧克力 ");
setPrice(3.0f); // 调味品的价格
}
}
package com.etc.design.decorator;
// 具体的Decorator(调味品)
public class Milk extends Decorator {
public Milk(Drink obj) {
super(obj);
setDes(" 牛奶 ");
setPrice(2.0f);
}
}
package com.etc.design.decorator;
// 具体的Decorator(调味品)
public class Soy extends Decorator{
public Soy(Drink obj) {
super(obj);
setDes(" 豆浆 ");
setPrice(1.5f);
}
}
package com.etc.design.decorator;
public class Client {
public static void main(String[] args) {
// 装饰者模式下的订单:2份巧克力+1份牛奶的美式咖啡
// 1. 点一份 美式咖啡
Drink order = new LongBlack();
System.out.println("美式咖啡 费用=" + order.cost());
System.out.println("美式咖啡 描述=" + order.getDes());
// 2. 美式咖啡加入一份牛奶
order = new Milk(order);
System.out.println("美式咖啡 加入1份牛奶 费用=" + order.cost());
System.out.println("美式咖啡 加入1份牛奶 描述= " + order.getDes());
// 3. 美式咖啡加入一份巧克力
order = new Chocolate(order);
System.out.println("美式咖啡 加入1份牛奶 加入1份巧克力 费用=" + order.cost());
System.out.println("美式咖啡 加入1份牛奶 加入1份巧克力 描述= " + order.getDes());
// 4. 美式咖啡加入一份巧克力
order = new Chocolate(order);
System.out.println("美式咖啡 加入1份牛奶 加入2份巧克力 费用=" + order.cost());
System.out.println("美式咖啡 加入1份牛奶 加入2份巧克力 描述= " + order.getDes());
System.out.println("===========================");
}
}

Java的IO结构中使用了装饰者模式,DataInputStream等类是被装饰者,而FilterInputStream是装饰者。

public abstract class InputStream implements Closeable {
......
}
public class FileInputStream extends InputStream {
......
}
public class FilterInputStream extends InputStream {
/**
* The input stream to be filtered.
*/
protected volatile InputStream in;
protected FilterInputStream(InputStream in) {
this.in = in;
}
......
}
public class DataInputStream extends FilterInputStream implements DataInput {
public DataInputStream(InputStream in) {
super(in);
}
......
}
package com.etc.uml.demo;
import java.io.DataInputStream;
import java.io.FileInputStream;
public class Decorator {
public static void main(String[] args) throws Exception{
//说明:
//1. InputStream 是抽象类, 类似方案3中的Drink类。
//2. FileInputStream 是被装饰者,是InputStream的子类,类似方案3中LongBlack、Espresso、Decaf。
//3. FilterInputStream 是装饰者,是InputStream的子类,类似方案3中的Decorator。在FilterInputStream类中有一个InputStream类的成员属性in,即 FilterInputStream 含有 被装饰者。
//4. DataInputStream 是具体的修饰者,是FilterInputStream的子类,类似方案3中的Milk、Soy、Chocolate。
DataInputStream dis = new DataInputStream(new FileInputStream("D:\\abc.txt"));
System.out.println(dis.read());
dis.close();
}
}