假设要开一家咖啡馆,流程是先选择一种咖啡,然后选择调料,调料不止一种,且可加也不可加。
如何设计:方案一:写一个基类,定义商品名字(description)和价格(cost),具体咖啡和调料都继承此基类。那么会发现,类非常多,意味着,维护变的更加困难。
方案二:在基类里进行判断是否添加调料,如hasMilk(),setMilk(),hasWhip(),setWhip(),那么只要具体的咖啡继承此基类就行了,所以类是减少了,但相应的问题就来了,如果某种调料不要了,或有新增了一种,如果调料想加两份, 调料价格改变等等。
也许要问,改就改了,感觉改的东西并不多啊,也不麻烦的样子,但是我们应该去尽力去遵守一项原则,*类应该对扩展开放,对修改关闭*。意思就是在不修改原有的代码情况下,可以搭配新的行为。
所以现在来认识装饰模式,这里分为装饰者和基本组件,多个装饰者或单个来装饰组件,这说明他们有同一个超类,装饰者可以附加上自己的责任行为,在上面例子里,咖啡就是组件,调料就是装饰者。 可以想象做菜,肉,蔬菜,鱼之类的是组件,而盐,黄酒,味精之类的是装饰者,不同的调料加上菜中,那么菜的味道特性就会发生改变。
装饰者模式:动态地将责任附加到对象上,若要扩展功能,装饰者提供了比继承更有弹性的替代方案。
何谓动态,在客户端上可以自己定义,责任附加,一般意味着在原有基础上对数据进行处理。
在Java API上I/O流就是运用了这个模式
以下是代码
系统段
顶层抽象类
public abstract class Beverage {
String description = "Unknow Beverage";
public String getDescription(){
return description;
}
public abstract double cost();
}
组件A
public class HouseBlend extends Beverage{
public HouseBlend(){
description = "House Blend Coffee";
}
@Override
public double cost() {
return 0.89;
}
}
组件B
public class Espresso extends Beverage{
public Espresso(){
description = "Espresso";
}
@Override
public double cost() {
return 1.99;
}
}
装饰者接口
public abstract class CondimentDecorator extends Beverage{
public abstract String getDescription();
}
装饰者A
public class Mocha extends CondimentDecorator{
Beverage beverage;
public Mocha(Beverage beverage){
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ",Mocha";
}
@Override
public double cost() {
return 0.20 + beverage.cost();
}
}
装饰者B
public class Sou extends CondimentDecorator{
Beverage beverage;
public Sou(Beverage beverage){
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription() + ",Sou";
}
@Override
public double cost() {
return 1.20 + beverage.cost();
}
}
客户端
public class StarbuzzCoffee {
public static void main(String[] args) {
Beverage beverage = new Espresso();
System.out.println(beverage.getDescription()
+"$" + beverage.cost());
Beverage beverage3 = new HouseBlend();
beverage3 = new Mocha(beverage3);
beverage3 = new Sou(beverage3);
System.out.println(beverage3.getDescription()
+"$" + beverage3.cost());
}
}