设计原则
类应该对扩展开发,对修改关闭
不修改现有代码的情况下,可以添加新的行为,这样的设计具有弹性,可以应对变化,提供新的功能。
装饰者模式完全遵循开放-关闭的原则
遵循开放-关闭原则,通常会引入新的抽象层次,增加代码的复杂度
应该把注意力集中在主要的容易发生变化的地方,然后应用开放-关闭原则
装饰:最低层的功能不变,对外提供了更灵活更方便的方法
委托:低层功能调用还得依赖与具有该行为的对象,委托这个对象去完成它自己才能完成的事
咖啡馆的故事
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"; } @Override public double cost() { return 1.99; } }
将要被装饰的类(二)
package decorator.coffee; public class HouseBlend extends Beverage { public HouseBlend() { this.description = "HouseBlend"; } @Override public 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;//参数为父类,为多态做准备 } @Override public 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传进来 } @Override public 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传进来 } @Override public 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传进来 } @Override public 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中对装饰者模式的应用