装饰者模式

1.简介

要求

咖啡种类/单品咖啡:Espresso(意大利浓咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡)
调料: Milk、Soy(豆浆)、Chocolate
要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便
使用OO的来计算不同种类咖啡的费用:客户可以点单品咖啡,也可以单品咖啡+调料组合。

方案一

image-20210306171648392

Drink是一个抽象类,表示饮料
des 就是对咖啡的描述,比如咖啡的名字
cost()方法就是计算费用,Drink类中做成一个抽象方法.
Decaf 就是单品咖啡,继承 Drink,并实现cost
Espress && Milk就是单品咖啡+调料,这个组合很多

问题:这样设计,会有很多类,当我们增加一个单品咖啡,或者一个新的调料,类的数量就会倍增,就会出现类爆炸

方案二

将调料内置Drink中

方案2-解决星巴克咖啡订单问题分析

1)方案2可以控制类的数量,不至于造成很多的类
2)在增加或者删除调料种类时,代码的维护量很大
3)考虑到用户可以添加多份调料时,可以将 hasMilk返回一个对应int
4)考虑使用装饰者模式

装饰者模式定义

  • 装饰者模式:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(ocp)

  • 这里提到的动态的将新功能附加到对象和ocp原则,在后面的应用实例上会以代码的形式体现。

装饰者模式原理

装饰者模式就像打包一个快递

  • 主体:比如:陶瓷、衣服(Component)//被装饰者
    包装:比如:报纸填充、塑料泡沫、纸板、木板(Decorator)
  • Component主体:比如类似前面的Drink
  • ConcreteComponent和 Decorator
    ConcreteComponent:具体的主体,比如前面的各个单品咖啡
    Decorator:装饰者,比如各调料.
  • 在Component与ConcreteComponent之间,如果ConcreteComponent类很多,还可以设计一个缓冲层,将共有的部分提取出来,抽象层一个类。

2.案例

image-20210306185944592

image-20210306193201890

Drink类

1
2
3
4
5
6
7
8
@Data
public abstract class Drink {
public String des; //描述
private float price = 0.0f;
//计算费用的抽象方法
//子类实现
public abstract float cost();
}

Coffee类

1
2
3
4
5
6
7
public class Coffee extends Drink{

@Override
public float cost() {
return super.getPrice();
}
}

两种单品咖啡

1
2
3
4
5
6
public class Espresso extends Coffee{
public Espresso(){
setDes("意大利咖啡");
setPrice(6.0f);
}
}
1
2
3
4
5
6
7
public class LongBlack extends Coffee {

public LongBlack(){
setDes("longback");
setPrice(5.0f);
}
}

装饰器类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Decorator extends Drink {
private Drink obj;

public Decorator(Drink obj) {
this.obj = obj;
}

@Override
public float cost() {
//getPrice得到自己的价格的价格
return super.getPrice() + obj.cost();
}

@Override
public String getDes() {
//装饰者描述+装饰者的价格+被装饰者的描述
return super.des + " " + super.getPrice() + "&&" + obj.getDes();
}
}

具体装饰器类

1
2
3
4
5
6
7
8
//具体的Decorator调味品
public class Chocolate extends Decorator {
public Chocolate(Drink obj) {
super(obj);
setDes("巧克力");
setPrice(3.0f); //调味品的价格
}
}
1
2
3
4
5
6
7
public class Milk extends Decorator {
public Milk(Drink obj) {
super(obj);
setDes("牛奶");
setPrice(2.0f); //调味品的价格
}
}

使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class CoffeeBar {
public static void main(String[] args) {
//订单:2分巧克力+一份牛奶+LongBlack
//1.点一份LongBlack
Drink order = new LongBlack();
System.out.println("费用:"+order.cost());
System.out.println("描述:"+order.getDes());
//2.加入一份牛奶
order = new Milk(order);
System.out.println("费用:"+order.cost());
System.out.println("描述:"+order.getDes());
//3.加入一份巧克力
order = new Chocolate(order);
System.out.println("费用:"+order.cost());
System.out.println("描述:"+order.getDes());
}
}
/*
费用:5.0
描述:longback
费用:7.0
描述:牛奶 2.0 && longback
费用:10.0
描述:巧克力 3.0 && 牛奶 2.0 && longback
*/

3.在JDK中的使用

Java中IO结果FilterputStream是一个装饰者者

image-20210306194248301
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
public class Decorator {

public static void main(String[] args) throws Exception {

//说明
//1 . InputStream是抽象类,类似我们前面讲的Drink
//2.FileInputStream是 ImputStream子类,类似我们前面的DeCaf, LongBlack
//3.FilterInputStream是 InputStream子类:类似我们前面的Decorator修饰者
//4. DataInputStream是 FilterInputStream子类,具体的修饰者,类似前面的Milk, Soy
//5.FilterInputStream类有protected volatile InputStream in;即含被装饰者
//6.分析得出在jdk 的io体系中,就是使用装饰者模式
DataInputStream dis =new DataInputStream(new FileInputStream(" d:llabc.txt"));System.out.println(dis.read());
dis.close();
}
}