1、星巴克咖啡订单项目
1)咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡)
2)调料:Milk、Soy(豆浆)、Chocolate
3)要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便
4)使用 OO 的来计算不同种类咖啡的费用:客户可以点单品咖啡,也可以单品咖啡+调料组合。
2、方案 1
2.1 思路分析:
方案 1-较差的方案
2.2 问题分析
1)Drink 是一个抽象类,表示饮料
2)description 就是对咖啡的描述,比如咖啡的名字
3)cost()方法就是计算费用,Drink 类做成一个抽象方法。
4)Decaf 就是单品咖啡,继承 Drink,并实现 cost()
5)Espresso && Milk 就是单品咖啡+调料,这个组合很多
6)问题:这样设计,会有很多类,当我们增加一个单品咖啡,或者一个新的调料,类的数量就会倍增,就会出现类爆炸。
3、方案 2
3.1 思路分析:
方案 2-解决星巴克咖啡订单项目(好点)
前面分析到方案 1 因为咖啡单品+调料组合会造成类的倍增,因此可以做改进,将调料内置到 Drink 类,这样就不会造成数量过多。从而提高项目的维护性:
说明:milk、soy、chocolate 可以设计为 boolean,表示是否要添加相应的调料。
3.2 问题分析
1)方案 2 可以控制类的数量,不至于造成很多的类
2)在增加或者删除调料种类时,代码的维护量很大
3)考虑到用户可以添加多份调料时,可以将 hasMilk 返回一个对应 int
4)考虑使用装饰者模式
4、装饰者模式
4.1 定义
1)装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,他比继承更有弹性,装饰着模式也体现了开闭原则(ocp)
2)这里提到的动态的将新功能附加到对象和 ocp 原则,在后面的应用实例上会以代码的形式体验,需要细细体会。
4.2 原理
1)装饰者模式就像打包一个快递
- 主体:比如陶瓷、衣服(Component) // 被装饰者
- 包装:比如报纸填充、塑料泡沫、纸板、木板(Decorator)// 装饰者
2)Component
主体:比如类似前面的 Drink
3)ConcreteComponent 和 Decorator
- ConcreteComponent:具体的主体,比如前面的各个单品咖啡
- Decorator:装饰者,比如各调料
4)在如图的 Component 与 ConcreteComponent 之间,如果 ConcreteComponent 类很多,还可以设计一个缓冲层,将共有的部分提取出来,抽象成一个类。
4.3 应用实例
1)思路分析
说明:
- Drink 类就是前面说的抽象类,Component
- ShortBlack 就是单品咖啡
- Decorator 是一个装饰类,含有一个被装饰的对象(Drink obj)
- Decorator 的 cost 方法进行一个费用的叠加计算,递归的计算架构
2)订单细节说明
装饰者模式下的订单:2 份巧克力+1 份牛奶的 LongBlack
说明:
- Milk 包含了 LongBlack
- 一份 Chocolate 包含了(Milk+LongBlack)
- 一份 Chocolate 包含了(Chocolate+Milk+LongBlack)
- 这样不管是什么形式的单品咖啡+调料组合,通过递归的方式可以方便的组合和维护
3)代码实现
Drink 饮料抽象类
package com.wenze.design.decorator;
import lombok.Getter;
import lombok.Setter;
/**
* 饮料抽象类
*
* @author wenze
* @version 1.0
* @Date 2023-10-25 18:37:13
* @since 1.0
*/
@Setter
@Getter
public abstract class Drink {
/**
* 描述
*/
public String description;
/**
* 价格
*/
private double price;
public abstract double cost();
}
Coffee 咖啡类
package com.wenze.design.decorator;
import com.wenze.design.decorator.Drink;
/**
* 咖啡类
*
* @author wenze
* @version 1.0
* @Date 2023-10-25 18:39:04
* @since 1.0
*/
public class Coffee extends Drink {
@Override
public double cost() {
return super.getPrice();
}
}
Espresso 意大利浓咖啡类
package com.wenze.design.decorator.coffee;
import com.wenze.design.decorator.Coffee;
/**
* 意大利浓咖啡
*
* @author wenze
* @version 1.0
* @Date 2023-10-25 18:39:31
* @since 1.0
*/
public class Espresso extends Coffee {
public Espresso() {
setDescription("意大利浓咖啡");
setPrice(6.0);
}
}
Decorator 装饰类
package com.wenze.design.decorator;
/**
* 装饰者
*
* @author wenze
* @version 1.0
* @Date 2023-10-25 18:42:35
* @since 1.0
*/
public class Decorator extends Drink {
private Drink obj;
public Decorator(final Drink obj) {
this.obj = obj;
}
@Override
public double cost() {
// 自己的价格 + drink 的价格
return super.getPrice() + obj.cost();
}
@Override
public String getDescription() {
// 自己的描述+自己的价格+被装饰者的描述
return super.description + " " + super.getPrice() + " && " + obj.getDescription();
}
}
Chocolate 巧克力类
package com.wenze.design.decorator.decorator;
import com.wenze.design.decorator.Decorator;
import com.wenze.design.decorator.Drink;
/**
* 巧克力,具体的Decorator,这里就是调味品
*
* @author wenze
* @version 1.0
* @Date 2023-10-25 18:51:04
* @since 1.0
*/
public class Chocolate extends Decorator {
public Chocolate(final Drink obj) {
super(obj);
setDescription("巧克力");
setPrice(3.0);
}
}
Milk 牛奶类
package com.wenze.design.decorator.decorator;
import com.wenze.design.decorator.Decorator;
import com.wenze.design.decorator.Drink;
/**
* 牛奶
*
* @author wenze
* @version 1.0
* @Date 2023-10-25 18:52:12
* @since 1.0
*/
public class Milk extends Decorator {
public Milk(final Drink obj) {
super(obj);
setDescription("牛奶");
setPrice(2.0);
}
}
CoffeeBar 咖啡店客户端类
package com.wenze.design.decorator;
import com.wenze.design.decorator.coffee.LongBlack;
import com.wenze.design.decorator.decorator.Chocolate;
import com.wenze.design.decorator.decorator.Milk;
/**
* 咖啡店,客户端
*
* @author wenze
* @version 1.0
* @Date 2023-10-25 18:53:19
* @since 1.0
*/
public class CoffeeBar {
public static void main(String[] args) {
// 2份巧克力+1 份牛奶+LongBlack
// 1. 点一份 LongBlack Drink order = new LongBlack();
// 2. 加入一份牛奶
order = new Milk(order);
// 3. 加入一份巧克力
order = new Chocolate(order);
// 4. 加入第二份巧克力
order = new Chocolate(order);
System.out.println("order price: " + order.cost());
System.out.println("order description: " + order.getDescription());
}
}
结果为:
5、装饰者模式在 JDK 应用的源码分析
Java 的 IO 结构,FilterInputStream 就是一个装饰者
// 是一个抽象类,即 Component
public abstract class InputStream implements Closeable{}
// 是一个装饰者类 Decorator
public class FilterInputStream extends InputStream {
// 被装饰的对象
protected volatile InputStream in;
}
// FilterInputStream子类,也继承了被装饰的对象 in
class DataInputStream extends FilterInputStream implements DataInput {}
说明:
- InputStream 是抽象类,类似我们前面的 Drink
- FileInputStream 是 InputStream 子类,类似前面的 Espresso,LongBlack
- FilterInputStream 是 InputStream 子类,类似前面的 Decorator 装饰者
- DataInputStream 是 FilterInputStream 子类,具体的修饰者,类似前面的 Milk等
- FilterInputStream 类 有
protected volatile InputStream in;
即含被装饰者 - 分析得出在 jdk 的 io 体系中,就是使用装饰者模式