设计原则
类应该对扩展开发,对修改关闭
不修改现有代码的情况下,可以添加新的行为,这样的设计具有弹性,可以应对变化,提供新的功能。
装饰者模式完全遵循开放-关闭的原则
遵循开放-关闭原则,通常会引入新的抽象层次,增加代码的复杂度
应该把注意力集中在主要的容易发生变化的地方,然后应用开放-关闭原则
装饰:最低层的功能不变,对外提供了更灵活更方便的方法
委托:低层功能调用还得依赖与具有该行为的对象,委托这个对象去完成它自己才能完成的事
咖啡馆的故事
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中对装饰者模式的应用