设计原则
类应该对扩展开发,对修改关闭
不修改现有代码的情况下,可以添加新的行为,这样的设计具有弹性,可以应对变化,提供新的功能。
装饰者模式完全遵循开放-关闭的原则
遵循开放-关闭原则,通常会引入新的抽象层次,增加代码的复杂度
应该把注意力集中在主要的容易发生变化的地方,然后应用开放-关闭原则
装饰:最低层的功能不变,对外提供了更灵活更方便的方法
委托:低层功能调用还得依赖与具有该行为的对象,委托这个对象去完成它自己才能完成的事
咖啡馆的故事
Beverage为所有类的基类,它将作为方法的参数接受各种类型的子类对象
HouseBend,DarkRoster,Espresso,Decaf,都是被装饰对象,通过cost方法计算各自的价钱
CondimentDecorator继承Beverage,自身为一个抽象类,为子类封装共有的属性和方法
Milk,Mocha,Soy,Whip,都是装饰者,将对被装饰对象进行装饰,在内部会让被装饰者去调用自己的方法计算价格
公共的基类,将来作为方法的参数,接收各种子类对象
package decorator.coffee;import java.text.NumberFormat;public abstract class Beverage {public static enum BeverageSize {BIG, MEDIUM, SMALL;// 大杯,中杯,小杯}//杯子大小private BeverageSize size;public BeverageSize getSize() {return size;}public void setSize(BeverageSize size) {this.size = size;}//饮料描述信息protected String description = "Unknow Beverage";public String getDescription() {return description;}//需由子类去实现去方法public abstract double cost();/*** 计算总价时,加上杯子的钱(只能加1次)* @return*/public double costTotal() {double total = cost();if(size!=null) {switch (this.size.ordinal()) {case 0:total += 0.20;//大杯break;case 1:total += 0.15;//中杯break;case 2:total += 0.10;//小杯break;}}NumberFormat nf = NumberFormat.getInstance();nf.setMaximumFractionDigits(2);return Double.valueOf(nf.format(total));}
}
将要被装饰的类(一)
package decorator.coffee;public class Espresso extends Beverage {public Espresso() {this.description = "Espresso";}@Overridepublic double cost() {return 1.99;}}
将要被装饰的类(二)
package decorator.coffee;public class HouseBlend extends Beverage {public HouseBlend() {this.description = "HouseBlend";}@Overridepublic double cost() {return 0.89;}}
中转作用的类,实现装饰者与被装饰者都是Beverage的子类
package decorator.condiment;import decorator.coffee.Beverage;/*** 继承Beverage,让装饰者继承它,这样装饰者(调料)与被装饰者(具体的饮料)都属于Beverage* 都是Beverage的子类,从而在面向父类/接口编程时,都可以作为参数传入到方法中!*/
public abstract class CondimentDecorator extends Beverage {//持有祖先的引用//通过祖先获取旁系,祖先对子孙的设计,这样便可以利用多态的功能,动态绑定子类对象了!//【这里的做法与javascript中getParentNode()获取sibing的思想有一丁点儿的类似】Beverage beverage;//实为组合的应用,特殊的地方:被组合的对象为自己的父类public CondimentDecorator(Beverage beverage) {this.beverage = beverage;//参数为父类,为多态做准备}@Overridepublic String getDescription() {return this.beverage.getDescription()+", "+this.getClass().getSimpleName();}}
装饰者(一)
package decorator.condiment;import decorator.coffee.Beverage;/*** 真正的装饰者**/
public class Mocha extends CondimentDecorator {public Mocha(Beverage beverage) {super(beverage);//将实际的coffee传进来}@Overridepublic double cost() {return 0.20 + this.beverage.cost();//delegate委托beverage的具体实现类去计算自己的价钱}}
装饰者(二)
package decorator.condiment;import decorator.coffee.Beverage;/*** 真正的装饰者**/
public class Soy extends CondimentDecorator {public Soy(Beverage beverage) {super(beverage);//将实际的coffee传进来}@Overridepublic double cost() {return 0.40 + this.beverage.cost();//delegate委托beverage的具体实现类去计算自己的价钱}}
装饰者(三)
package decorator.condiment;import decorator.coffee.Beverage;/*** 真正的装饰者**/
public class Whip extends CondimentDecorator {public Whip(Beverage beverage) {super(beverage);//将实际的coffee传进来}@Overridepublic double cost() {return 0.30 + this.beverage.cost();//delegate委托beverage的具体实现类去计算自己的价钱}}
测试
package test;import decorator.coffee.Beverage;
import decorator.coffee.Beverage.BeverageSize;
import decorator.coffee.Espresso;
import decorator.condiment.Mocha;public class BeverageTest {public static void main(String[] args) {Beverage beverage = new Espresso();System.out.println(beverage.getDescription()+" $"+beverage.costTotal());beverage = new Mocha(beverage);System.out.println(beverage.getDescription()+" $"+beverage.costTotal());beverage = new Mocha(beverage);System.out.println(beverage.getDescription()+" $"+beverage.costTotal());//设置杯子的大小beverage.setSize(BeverageSize.BIG);System.out.println(beverage.getDescription()+" $"+beverage.costTotal());}
}
JAVA I/O中对装饰者模式的应用