以咖啡订单引入装饰者模式:
- 咖啡种类:Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡)
- 调料:Milk、Soy(豆浆)、Chocolate
- 要求:在扩展新的咖啡种类的时候,具有良好扩展性,改动方便,维护方便
- 使用面向对象思想来计算不同咖啡的费用:客户可以点单品咖啡,也可以单品咖啡+调料组合
1.结构型模式描述
如何将类或对象按某种布局组成更大的结构
。它分为类结构型模式
和对象结构型模式
,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象
2.由于
组合关系或聚合关系比继承关系耦合度低
,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性
1.装饰者模式
1.1.定义:
- 动态的将新功能附加到对象上, 在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)
2.2.结构:
抽象构件(Component)角色
:定义一个抽象接口以规范准备接收附加责任的对象,比如类似前面的 Drink
具体构件(Concrete Component)角色
:实现抽象构件,通过装饰角色为其添加一些职责,比如前面的各个单品咖啡
抽象装饰(Decorator)角色
:继承或实现抽象构件
,并包含具体构件的实例,可以通过其子类扩展具体构件的功能具体装饰(ConcreteDecorator)角色
:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任,
装饰者模式就
像打包一个快递
- 主体:比如陶瓷、衣服 (Component) // 被装饰者
- 包装:比如报纸填充、塑料泡沫、纸板、木板(Decorator)
如果具体构件类很多,
还可以设计一个缓冲层,将共有的部分提取出来,抽象成一个类,就是抽象装饰着
2.案例实现:
2.1.传统方案1:
a.类图设计:
- 1.Drink是一个抽象类,表示饮料
- 2.des是对咖啡的描述,如咖啡的名称
- 3.cost方法是计算费用,Drink类做成一个抽象方法,具体的咖啡种类通过继承抽象的Drink类来实现
b.缺点:
- 1.比较差的设计样例,耦合度过高
- 2.如果说要添加扩展新的单品咖啡或者组合口味的咖啡的时候,咖啡的种类会很多,咖啡+调料的组合会更多,那么就会导致
类的数量增加,就会引发"类爆炸"
2.2.传统方案2:
a.类图:
- 将调料内置到Drink类
b.缺点:
- 将调料内置到Drink类,不会造成类数量过多,但是添加或者删除调味品的种类时,需要改动维护,代码的维护量也很大
2.3.装饰者模式实现:
a.类图设计:
b.编码实现:
- 1.抽象构件(Component)角色:抽象类饮品
public abstract class Drink {
//对饮品的描述
private String description;
//价格
private float price;
//计算价格,由子类实现
public abstract float cost();
public void setDescription(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
public void setPrice(float price) {
this.price = price;
}
public float getPrice() {
return price;
}
}
- 2.对众多具体构件的抽象:缓冲类Coffee
public class Coffee extends Drink{
@Override
public float cost() {
return super.getPrice();
}
}
- 3.具体构件类:两种Coffee
public class LongBlackCoffee extends Coffee{
public LongBlackCoffee(){
setDescription("一份美式咖啡");
setPrice(7.0f);
}
}
public class ShortBlackCoffee extends Coffee {
public ShortBlackCoffee(){
setDescription("一份浓缩咖啡");
setPrice(5.0f);
}
}
- 4.装饰者Decorator类:需要继承和聚合被装饰类
//装饰者
public class Decorator extends Drink{
//被装饰者;
private Drink drink;
public Decorator(Drink drink){
this.drink = drink;
}
@Override
public float cost() {
return super.getPrice() + drink.cost();
}
@Override
public String getDescription(){
return drink.getDescription() + " && " +
super.getDescription() + super.getPrice();
}
}
- 5.两种调味品:
public class Milk extends Decorator{
public Milk(Drink drink) {
super(drink);
setDescription("一份牛奶");
setPrice(2.0f);
}
}
public class Chocolate extends Decorator{
public Chocolate(Drink drink) {
super(drink);
setDescription("一份巧克力");
setPrice(1.0f);
}
}
c.
- 1.模拟客户下单:加两份巧克力和一份牛奶的咖啡
public class Client {
public static void main(String[] args) {
//两份巧克力+一份牛奶的美式咖啡;
//1.先点美式咖啡;
Drink result = new LongBlackCoffee();
System.out.println("步骤一::" + result.getDescription());
System.out.println("本次花费==>" + result.cost());
//2.加份牛奶;
result = new Milk(result);
System.out.println("步骤二::" + result.getDescription());
System.out.println("本次花费==>" + result.cost());
//3.加份巧克力;
result = new Chocolate(result);
System.out.println("步骤三::" + result.getDescription());
System.out.println("本次花费==>" + result.cost());
//4.加份巧克力;
result = new Chocolate(result);
System.out.println("步骤四::" + result.getDescription());
System.out.println("最终花费==>" + result.cost());
}
}
- 2.输出
步骤一::一份美式咖啡
本次花费==>7.0
步骤二::一份美式咖啡 && 一份牛奶2.0
本次花费==>9.0
步骤三::一份美式咖啡 && 一份牛奶2.0 && 一份巧克力1.0
本次花费==>10.0
步骤四::一份美式咖啡 && 一份牛奶2.0 && 一份巧克力1.0 && 一份巧克力1.0
最终花费==>11.0
3.装饰者模式在 JDK 应用的源码分析
3.1.InputStream类图分析:
- 1.Java 的 IO 结构,
FilterInputStream
就是一个装饰者
public class Test {
public static void main(String[] args) throws IOException {
DataInputStream dataInputStream = new DataInputStream(new FileInputStream("e:\\a.txt"));
System.out.println(dataInputStream.read());
dataInputStream.close();
}
}
3.2.说明:
InputStream
是抽象类,类似前面的DrinkFileInputStream、StringBufferInputStream等
是InputStream子类,类似前面的浓缩咖啡,美式咖啡FilterInputStream
是InputStream子类,类似前面的Decorator修饰者DataInputStream等
是FilterInputStream子类,具体的修饰者,类似前面的Milk,Chocolate等FilterInputStream类有protected volatile InputStream in
; 即含被装饰者
public class FilterInputStream extends InputStream {
protected volatile InputStream in;
protected FilterInputStream(InputStream var1) {
this.in = var1;
}
分析得出在 JDK 的 IO 体系中,就是使用装饰者模式