1.问题
咖啡订单项目:
- 咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡)
- 调料:Milk、Soy(豆浆)、Chocolate
- 要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便
- 使用 OO 的来计算不同种类咖啡的费用:客户可以点单品咖啡,也可以单品咖啡+调料组合。
1.1 方案一
问题解析:
- Drink 是一个抽象类,表示饮料
- des 就是对咖啡的描述,比如咖啡的名字
- cost()方法就是计算费用,Drink 类中做成一个抽象方法
- Decaf 就是单品咖啡, 继承 Drink,并实现 cost
- Espress && Milk 就是单品咖啡+调料,这个组合很多,容易产生过多的子类
1.2 方案二
这里的milk、soy、chocolate都是Boolean类型
问题解析:
- 方案2可以控制类的数量,不至于造成很多的类在增加或者删除调料种类时,代码的维护量很大
- 考虑到用户可以添加多份 调料时,可以将 hasMilk 返回一个对应 int
- 考虑使用 装饰者 模式
2.装饰者模式
2.1 基本介绍
- 装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)
- 这里提到的动态的将新功能附加到对象和 ocp 原则
2.2 结构
装饰(Decorator)模式中的角色:
- 抽象构件(Component)角色 :定义一个抽象接口以规范准备接收附加责任的对象。
- 具体构件(Concrete Component)角色 :实现抽象构件,通过装饰角色为其添加一些职责。
- 抽象装饰(Decorator)角色 : 继承或实现抽象构件,并包含具体构件的实例,可以通过其子
类扩展具体构件的功能。 - 具体装饰(ConcreteDecorator)角色 :实现抽象装饰的相关方法,并给具体构件对象添加附
加的责任。
2.3 代码实现
类图展示:
抽象类Drink:
public abstract class Drink {
private String description;
private double price;
public abstract double cost();
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
}
装饰者:
public class Decorator extends Drink {
private Drink drink;
public Decorator(Drink drink) {
this.drink = drink;
}
@Override
public double cost() {
return super.getPrice() + drink.cost();
}
@Override
public String getDescription() {
return super.getDescription() + " " + getPrice() + " && " + drink.getDescription();
}
}
Coffee:
public class Coffee extends Drink {
@Override
public double cost() {
return super.getPrice();
}
}
Coffee子类:
public class Decaf extends Coffee {
public Decaf() {
setDescription("无因咖啡");
setPrice(1);
}
}
public class Espresso extends Coffee {
public Espresso() {
setDescription("意大利咖啡");
setPrice(6);
}
}
public class LongBlack extends Coffee {
public LongBlack() {
setDescription("longBlack");
setPrice(5);
}
}
public class ShortBlack extends Coffee {
public ShortBlack() {
setDescription("shortBlack");
setPrice(4);
}
}
装饰器子类:
public class Soy extends Decorator {
public Soy(Drink drink) {
super(drink);
setDescription("豆浆");
setPrice(1.5);
}
}
public class Milk extends Decorator{
public Milk(Drink drink) {
super(drink);
setDescription("牛奶");
setPrice(2);
}
}
public class Chocolate extends Decorator {
public Chocolate(Drink drink) {
super(drink);
setDescription("巧克力");
setPrice(3);
}
}
Client调用:
public class Client {
public static void main(String[] args) {
// 两份巧克力+一份牛奶Longblack
Drink order1 = new LongBlack();
System.out.println("order1费用:" + order1.cost());
System.out.println("order1描述:" + order1.getDescription());
order1 = new Milk(order1);
System.out.println("order1加一份牛奶费用:" + order1.cost());
System.out.println("order1加一份牛奶描述:" + order1.getDescription());
order1 = new Chocolate(order1);
System.out.println("order1加一份牛奶,加一份巧克力费用:" + order1.cost());
System.out.println("order1加一份牛奶,加一份巧克力描述:" + order1.getDescription());
order1 = new Chocolate(order1);
System.out.println("order1加一份牛奶,加两份巧克力费用:" + order1.cost());
System.out.println("order1加一份牛奶,加两份巧克力描述:" + order1.getDescription());
System.out.println("==============================");
Drink order2 = new Decaf();
System.out.println("无因咖啡费用:" + order2.cost());
System.out.println("无因咖啡描述:" + order2.getDescription());
order2 = new Milk(order2);
System.out.println("无因咖啡,加一份牛奶费用:" + order2.cost());
System.out.println("无因咖啡,加一份牛奶描述:" + order2.getDescription());
}
}
结果打印:
order1费用:5.0
order1描述:longBlack
order1加一份牛奶费用:7.0
order1加一份牛奶描述:牛奶 2.0 && longBlack
order1加一份牛奶,加一份巧克力费用:10.0
order1加一份牛奶,加一份巧克力描述:巧克力 3.0 && 牛奶 2.0 && longBlack
order1加一份牛奶,加两份巧克力费用:13.0
order1加一份牛奶,加两份巧克力描述:巧克力 3.0 && 巧克力 3.0 && 牛奶 2.0 && longBlack
==============================
无因咖啡费用:1.0
无因咖啡描述:无因咖啡
无因咖啡,加一份牛奶费用:3.0
无因咖啡,加一份牛奶描述:牛奶 2.0 && 无因咖啡
3.小结
- 装饰者模式可以带来比继承更加灵活性的扩展功能,使用更加方便,可以通过组合不同的装饰者对象来获取具有不同行为状态的多样化的结果。装饰者模式比继承更具良好的扩展性,完美的遵循开闭原则,继承是静态的附加责任,装饰者则是动态的附加责任。
- 装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。