网上关于装饰器(decorator)模式的教程很多,但是很少有能讲的很清楚的,所以有了这篇文章。
在讲解装饰器模式之前,先看看为什么要使用装饰器模式。
假设我们要为一个咖啡馆设计一套下单和结算系统,咖啡管的产品有茶,咖啡,橙汁三种产品,售价分别为5,10,15, 客人可以在饮料中加入一些搭配,比如咖啡可以加奶、加糖,橙汁可以加冰,茶也可以加奶。假设客人需要付费才能加配料。那我们先来思考一下如何设计这么一个系统,一个朴素的想法就是用组合模式,请看下面的uml图。
在每个饮料的基类中放一个List<Add> 表示附加的东西。来看看这么设计的问题:
- 如果我们规定了茶里面就是不能加糖的,这种组合无效。
- 假设我们有一天突发奇想,发明了一种新的饮料,咖啡+茶,那么这种设计显然不够灵活。
为了解决第二个问题,我们将所有的Berverage 和 add进行组合,枚举所有的产品,这显然不可行。
于是我们有了装饰器模式。请看下面的UML图:
这里的核心思想是:所有的饮料子类和附件类 既是Berverage,也包含一个Berverage,这一点非常重要,是装饰器模式的核心思想,子类 has-a 并且 is-a 父类。
来看看代码
public interface IBeverage {
public String getDescription();
public int cost();
}
public class Coffee implements IBeverage {
@Override
public String getDescription() {
return "to Coffee";
}
@Override
public int cost() {
return 12;
}
}
public class Tea implements IBeverage {
@Override
public String getDescription() {
return " to Tea";
}
@Override
public int cost() {
return 10;
}
}
// decorator not only is a kind of beverage, but has a kinds of beverage. The beverage addon decorator itself can wrap
// outside the beverage it contains. The beverage it contains can also wrap around some other beverage inside it,
// layer by layer
public class AddMilkDecorator implements IBeverage {
private IBeverage beverage;
public AddMilkDecorator(IBeverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return "add some milk" + this.beverage.getDescription();
}
@Override
public int cost() {
return 5+this.beverage.cost();
}
}
public class AddSugarDecorator implements IBeverage {
private IBeverage beverage;
public AddSugarDecorator(IBeverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return "Add sugar" + this.beverage.getDescription();
}
@Override
public int cost() {
return 3 + this.beverage.cost();
}
}
public class Client {
public static void main(String[] args){
IBeverage beverage = new AddMilkDecorator(new AddMilkDecorator(new AddSugarDecorator(new Coffee())));
System.out.println("Your order is " + beverage.getDescription());
System.out.println("This beverage and addons totally costs"+beverage.cost());
}
}
好了,先就讲这么多,总之只要记住:子类既is-a基类又has-a基类