一、咖啡馆订单系统项目
- 咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡)
- 调料:Milk、Soy(豆浆)、Chocolate
- 要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便
- 使用 OO 的来计算不同种类咖啡的费用: 客户可以点单品咖啡,也可以单品咖啡+调料组合。
1、解决方案一:
问题分析:
- Drink 是一个抽象类,表示饮料
- des 就是对咖啡的描述, 比如咖啡的名字
- cost() 方法就是计算费用,Drink 类中做成一个抽象方法.
- Decaf 就是单品咖啡, 继承 Drink, 并实现 cost
- Espress && Milk 就是单品咖啡+调料, 这个组合很多
- 问题:这样设计,会有很多类,当我们增加一个单品咖啡,或者一个新的调料,类的数量就会倍增。
二、装饰者模式定义
- 装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)
- 这里提到的动态的将新功能附加到对象和 ocp 原则,在后面的应用实例上会以代码的形式体现,请同学们注意体会。
装饰者模式原理:
- 装饰者模式就像打包一个快递
主体:比如:陶瓷、衣服 (Component) // 被装饰者
包装:比如:报纸填充、塑料泡沫、纸板、木板(Decorator) - Component 主体:比如类似前面的 Drink
- ConcreteComponent 和 Decorator
ConcreteComponent:具体的主体, 比如前面的各个单品咖啡 - Decorator: 装饰者,比如各调料
三、装饰者模式解决星巴克咖啡订单
需求:
装饰者模式下的订单:2 份巧克力+一份牛奶的 LongBlack
代码实现:
抽象超类Drink:
//抽象超类
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();
}
被装饰者主体抽象类Coffee:
//被装饰者主体抽象类,继承于Drink
public class Coffee extends Drink {
@Override
public float cost() {
return super.getPrice();
}
具体的饮料类:
//低咖啡因咖啡类(一种具体的饮料)
public class DeCaf extends Coffee {
public DeCaf() {
setDes(" 无因咖啡 "); setPrice(1.0f);
}
}
//意大利咖啡类(一种具体的饮料)
public class Espresso extends Coffee {
public Espresso() {
setDes(" 意大利咖啡 ");
setPrice(6.0f);
}
}
//longblack(一种具体的饮料)
public class LongBlack extends Coffee {
public LongBlack() {
setDes(" longblack ");
setPrice(5.0f);
}
}
调料装饰着抽象类,继承自饮料抽象类:
//调料装饰着抽象类(继承自饮料抽象类)
public class Decorator extends Drink {
private Drink drink;
public Decorator(Drink drink) {
this.drink = drink;
}
@Override
public float cost() {
// getPrice 自己价格
return getPrice() + drink.cost();
}
@Override
public String getDes() {
// drink.getDes() 输出被装饰者的信息
return des + " " + getPrice() + " && " + drink.getDes();
}
}
具体的配料类:
//巧克力调料类(继承自Decorator)
public class Chocolate extends Decorator {
public Chocolate(Drink drink) {
super(drink);
setDes(" 巧克力 ");
setPrice(3.0f); // 调味品 的价格
}
}
//巧克力调料类(继承自Decorator)
public class Milk extends Decorator {
public Milk(Drink drink) {
super(drink);
setDes(" 牛 奶 ");
setPrice(2.0f);
}
}
//豆浆调料类(继承自Decorator)
public class Soy extends Decorator {
public Soy(Drink drink) {
super(drink);
setDes(" 豆浆 ");
setPrice(1.5f);
}
}
客户端:
public class CoffeeBar {
public static void main(String[] args) {
// 装饰者模式下的订单:2 份巧克力+一份牛奶的 LongBlack
Drink drink = null;
// 1. 点一份 LongBlack
drink = new LongBlack();
System.out.println("费用 1=" + drink.cost());
System.out.println("描述=" + drink.getDes());
// 2. order 加入一份牛奶
drink = new Milk(drink);
System.out.println("drink 加入一份牛奶 费用 =" + drink.cost());
System.out.println("drink 加入一份牛奶 描述 = " + drink.getDes());
// 3. order 加入一份巧克力
drink = new Chocolate(drink);
System.out.println("drink 加入一份牛奶 加入一份巧克力 费 用 =" + drink.cost());
System.out.println("drink 加入一份牛奶 加入一份巧克力 描述 = " + drink.getDes());
// 4. order 加入一份巧克力
drink = new Chocolate(drink);
System.out.println("drink 加入一份牛奶 加入一份巧克力 费 用 =" + drink.cost());
System.out.println("drink 加入一份牛奶 加入一份巧克力 描述 = " + drink.getDes());
}
}
测试结果:
费用 1=5.0
描述= longblack
drink 加入一份牛奶 费用 =7.0
drink 加入一份牛奶 描述 = 牛 奶 2.0 && longblack
drink 加入一份牛奶 加入一份巧克力 费 用 =10.0
drink 加入一份牛奶 加入一份巧克力 描述 = 巧克力 3.0 && 牛 奶 2.0 && longblack
drink 加入一份牛奶 加入一份巧克力 费 用 =13.0
drink 加入一份牛奶 加入一份巧克力 描述 = 巧克力 3.0 && 巧克力 3.0 && 牛 奶 2.0 && longblack
装饰者用到的设计原则:
1、多用组合,少用继承。
2、对扩展开放,对修改关闭。