开始介绍之前,先分析一个实例:
现有一个咖啡订单项目:
(1)咖啡种类/单品咖啡:Espresso(意大利咖啡)、ShortBlack、LongBlack(美式咖啡)、Decaf(无因咖啡)。
(2)调料:Milk、Soy(豆浆)、Chocolate
(3)要求在扩展新的咖啡种类时,具有良好的扩展性、改动方便、维护方便。
(4)使用OO来计算不同种类咖啡的费用:客户可以点单品咖啡,也可以单品咖啡+调料组合。
针对上述项目,很容易想到可以使用一个抽象类drink来表示饮料,提供抽象的cost方法,各种单品咖啡,以及各种组合继承此抽象类,重写cost方法,便可计算出每种咖啡的费用。但是,此方案会产生很多的类,当我们增加一个单品咖啡或者一个新的调料时,会出现类爆炸现象。
针对上述问题,做一下改进:将调料内置到Drink类,并且为每种调料提供相应的方法来判断是否添加,这样便不会造成类数量过多,从而提高项目维护性。此方法虽然不会产生类爆炸现象,但是在增加调料的时候代码维护量很大。可进一步使用装饰者模式来改进。
基本介绍
装饰者模式:动态地将功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则。
原理介绍
装饰者模式就像打包一个快递:
主体(被装饰者):比如:衣服,零食等等。
包装(装饰者):比如:纸箱,塑料等等。
其原理图如下:
如图所示:
(1)Component:主体,比如前面实例中的Drink。
(2)ConcreteComponent:具体的主体,比如前面实例中的各个单品咖啡。
(3)Decorator:装饰者,比如前面实例中的调料。
(4)在如图的Component与ConcreteConponent之间,如果ConcreteConponent类很多,还可以设计一个缓冲层,将共有的部分提取出来,抽象成一个类。
案例分析
将文章开始的案例使用装饰者模式实现:
// 主体
public abstract class Drink {
private String design;
private float price = 0.0f;
public String getDesign() {
return design;
}
public void setDesign(String design) {
this.design = design;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
public abstract float cost();
}
// 缓冲层
public class Coffee extends Drink {
@Override
public float cost() {
return super.getPrice();
}
}
// 具体的主体(各种单品咖啡)
public class Espresso extends Coffee {
public Espresso() {
setDesign("意大利咖啡");
setPrice(10.0f);
}
}
public class LongBlack extends Coffee {
public LongBlack() {
setDesign("美式咖啡");
setPrice(11.0f);
}
}
// 装饰者
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 getDesign() {
return super.getDesign() + " " + super.getPrice();
}
}
// 具体的装饰者(调料)
public class Milk extends Decorator {
public Milk(Drink drink) {
super(drink);
setDesign("牛奶");
setPrice(5.0f);
}
}
public class Soy extends Decorator {
public Soy(Drink drink) {
super(drink);
setDesign("豆浆");
setPrice(2.0f);
}
}
public class Chocolate extends Decorator {
public Chocolate(Drink drink) {
super(drink);
setDesign("巧克力");
setPrice(8.0f);
}
}
// 主程序(一杯一份牛奶,两份巧克力的美式咖啡)
public static void main(String[] args) {
Drink longBlack = new LongBlack();
System.out.println(longBlack.getDesign() + " " + longBlack.cost());
longBlack = new Milk(longBlack);
System.out.println(longBlack.getDesign());
System.out.println("共计:" + longBlack.cost());
longBlack = new Chocolate(longBlack);
System.out.println(longBlack.getDesign());
System.out.println("共计:" + longBlack.cost());
longBlack = new Chocolate(longBlack);
System.out.println(longBlack.getDesign());
System.out.println("共计:" + longBlack.cost());
}
// 运行结果
/**
美式咖啡 11.0
牛奶 5.0
共计:16.0
巧克力 8.0
共计:24.0
巧克力 8.0
共计:32.0
*/
总结
JDK中FilterInputStream使用了装饰者模式。分析:
(1)InputStream是抽象类,类似于前面实例中的drink。
(2)FileInputStream是InputStream子类,类似前面实例中的LongBlack。
(3)FilterInputStream是InputStream子类,类似前面实例中的Decorator修饰者。
(4)DataInputStream是FilterInputStream子类,具体的修饰者,类似前面实例中的调料。
(5)FilterInputStream有protected volatile InputStream in,即含被装饰者。