一:装饰者模式
将最初的对象用一层一层外衣(修饰者)包装起来,实现自由组合。
举个栗子:
最初的对象:咖啡
外衣:糖,牛奶,豆浆…
我可以将上面的类型自由组合,比如
咖啡加糖和牛奶
或者 咖啡加豆浆
或者 咖啡加糖和豆浆
或者 ...
。
二:不使用装饰者模式
实现上面的栗子,如果没有使用装饰者模式,类图可能是这样:
缺点非常明显:
- 有多少种组合就必须自己实现多少个子类
- 设计子类时必须严格考虑,比如没有人在咖啡中
加牛奶又加豆浆
- 子类的
cost()
方法实现具体价格的计算,那要是牛奶价格上升,调整与牛奶相关的每一个子类中的cost()
方法将会使你奔溃。
三:使用装饰者模式
基本原则是我们定义一种公共类型A
,每个A
中有相同的方法,参数都是同一种类型A
,这样我们可以一层一层在原始咖啡上加东西。假设像下面这样调用我就能返回相应的价格。
CoffeeWithMilk = new Milk(new Coffee()); //CoffeeWithMilk.cost() 就能返回`咖啡加牛奶`价格
再来个稍微复杂的
CoffeeWithMilkAndSugar = new Milk(new Sugar(new Coffee()));
// 同理 CoffeeWithMilkAndSugar.cost()就能返回`咖啡加牛奶加糖`的价格。
那要怎么实现呢:
Beverage
是饮料类,也是基类。派生出了两种咖啡类Espresso
和Decaf
,当然它也可以派生别的饮料,比如茶
等等。CondimentDecorator
叫调料装饰类,之所以要定义它,表明它的子类都是用来修饰饮料的,我们也可以让Mocha
和Milk
直接继承自Beverage
,但是这样以后可能不太方便。
现在我的系统变的可以自由组合了。
四:代码如下
Beverage.java
public abstract class Beverage {
String Description = "";
public String getDescription() {
return Description;
}
public abstract double cost();
}
Espresso.java
public class Espresso extends Beverage {
private static final double CostOfEspresso = 1.22;
public Espresso() {
Description = "Espresson";
}
public double cost() {
return CostOfEspresso;
}
}
Decaf.java
public class Decaf extends Beverage {
private static final double CostOfDecaf = 1.55;
public Decaf() {
Description = "Decaf";
}
public double cost() {
return CostOfDecaf;
}
}
CondimentDecorator.java
public abstract class CondimentDecorator extends Beverage {
public abstract String getDescription();
}
Mocha.java
public class Mocha extends Beverage {
private static final double CostOfMocha = 0.22;
Beverage beverage;
public Mocha(Beverage be) {
this.beverage = be;
}
public String getDescription() {
return beverage.getDescription() + "";
}
public double cost() {
return beverage.cost()+ CostOfMocha;
}
}
Milk.java
public class Milk extends CondimentDecorator {
private static final double CostOfMilk = 0.33;
Beverage beverage;
public Milk(Beverage be) {
this.beverage = be;
}
public String getDescription() {
return beverage.getDescription() + "Milk,";
}
public double cost() {
return beverage.cost()+CostOfMilk;
}
}
mian.java
public class main {
public static void main(String[] args) {
Beverage cof = new Espresso();
System.out.println(cof.getDescription()+ " $: "+ cof.cost());
Beverage cof1 = new Milk(new Decaf());
System.out.println(cof1.getDescription()+ " $: "+ cof1.cost());
Beverage cof2 = new Milk(new Mocha(new Espresso()));
System.out.println(cof2.getDescription()+ " $: "+ cof2.cost());
}
}
五 :优点
优点(针对上面的缺点):
- 不用再设计那么多子类了,只需要有最基础的每个子类就好,少了组合的数量。
- 可以自由组合,我们大不了不会喝
牛奶加豆浆
罢了,如果真有人喝,那我们也可以组合出来。 - 现在如果牛奶涨价,只用在
Milk.java
中将它的价格CostOfMilk
更新即可,别的都不用动,是不是非常方便。
六:后续工作
JDK
中的I/O类
大量使用了装饰者模式,后续看看。