装饰者模式(Decorate Pattern)
装饰者模式定义:装饰者模式动态的将责任附加到对象上,若要扩展功能,装饰者提供了比继承更有弹性的替代方案
著名的咖啡屋示例,咖啡屋订单系统要求能够计算出每种咖啡的价格(个人感觉3种设计的主要差别在于抽象方式不同):
总共有3种饮品(HouseBlend,Decaf,Espresso),2种配料(Mocha,Whip)
设计一:
(咖啡 = 饮品,咖啡+配料 = 饮品,在抽象的过程中并没有考虑咖啡和配料之间的关系)
类太多,太复杂,如果增加一种饮品,增加一种配料,或者某一种饮品、配料的价格发生改变,要找到所有牵扯到的部分一一修改
设计二:
(配料抽象为饮品的属性)
着实简洁了不少:
public abstract class Beverage { public String description; private boolean mocha; private boolean whip; public String getDescription(){ return this.description; }; public void addMocha(){ this.mocha = true; } public void addWhip(){ this.whip = true; } public double cost(){ double price = 0d; if(mocha){ price += 0.5; } if(whip){ price += 0.1; } return price; } }
public class Decaf extends Beverage { public Decaf() { this.description = "Decaf"; } public double cost(){ double price = super.cost(); price += 2.5; return price; } }
public class HomeBlend extends Beverage { public HomeBlend() { this.description = "HomeBlend"; } public double cost(){ double price = super.cost(); price += 2.0; return price; } }
public class Espresso extends Beverage { public Espresso() { this.description = "Espresso"; } public double cost(){ double price = super.cost(); price += 2.2; return price; } }
测试一下:
public class Test { public static void main(String[] args) { Beverage b = new HomeBlend(); b.addMocha(); b.addWhip(); System.out.println(b.cost()); } }
2.6
考虑如下场景:
1,配料价格、种类的变化会迫使我们修改Beverage类,与此同时,当配料种类较多的时候,Beverage类将会变得相当庞大,难以维护
2,双倍mocha的Espresso怎么处理?
3,新饮品Icetea是不能加Mocha的,但是由于继承自Beverage类,依然获得了addMocha()方法,此时需要重写一个空方法,这种情况很常见,修改虽然不复杂但是显然设计二的可扩展性不高
设计三--装饰者模式:
装饰者模式分为3个部分:
1,抽象组件 -- 对应Beverage类
2,具体组件 -- 对应具体的饮品,如:HouseBlend,Decaf,Espresso
3,装饰着 -- 对应配料,如:Mocha,Whip
装饰者模式有3个特点:
1,具体组件和装饰者都继承自抽象组件(HouseBlend和Mocha都继承自Beverage)
2,可以使用装饰者组合具体组件创造出新的类(Mocha组合HouseBlend创造出“HouseBlend+Mocha”)
3,过程2可以重复,直到创造出需要的类
使用Decorate Pattern来获得一个Double Mocha Whip Espresso:
(咖啡= 饮品,配料 = 饮品,咖啡和配料本质上没有区别)
public abstract class Beverage { public String description; public String getDescription(){ return this.description; }; public abstract double cost(); }
public class Espresso extends Beverage { public Espresso() { this.description = "Espresso"; } public double cost(){ return 2.2; } }
public abstract class BeverageDecorator extends Beverage{ public abstract String getDescription(); }
public class Mocha extends BeverageDecorator { private Beverage b; public Mocha(Beverage b){ this.b = b; } public String getDescription() { return b.getDescription()+",Mocha"; } public double cost() { return b.cost() + 0.5; } }
测试一下:
public class Test { public static void main(String[] args) { Beverage b = new Espresso(); b = new Mocha(b); b = new Mocha(b); b = new Whip(b); System.out.println(b.getDescription()); System.out.println(b.cost()); } }
Espresso,Mocha,Mocha,Whip 3.3000000000000003
当然decorator中可以重写父类的方法,也可以扩展自己需要的方法:
public class Mocha extends BeverageDecorator { private Beverage b; public Mocha(Beverage b){ this.b = b; } public String getDescription() { return b.getDescription()+",Mocha"; } public double cost() { return b.cost() + 0.5; } public String getFeeling(){ return "tasty"; } }
public class Test { public static void main(String[] args) { Beverage b = new Espresso(); b = new Mocha(b); Mocha m = new Mocha(b); System.out.println(m.getDescription()); System.out.println(m.cost()); System.out.println(m.getFeeling()); Whip w = new Whip(m); System.out.println(w.getDescription()); System.out.println(w.cost()); } }
想要使用扩展方法,则必须针对特定类,并且扩展方法的使用受到限制(Whip w = new Whip(m);之后,m扩展的getFeeling()方法将不再能使用),当然如果能确定装饰者是叶子节点的话,这也不算是个问题
Espresso,Mocha,Mocha 3.2 tasty Espresso,Mocha,Mocha,Whip 3.3000000000000003
ps:
装饰者模式的缺点:
1,装饰者模式虽然扩展性较高,但是没有设计二简洁,类的数量略多(但肯定比设计一少很多),如何取舍扩展性和简洁是个问题,有所选择就要有所牺牲
2,很难搞清楚一个类究竟被装饰了多少层,可能是1层,也可能是100层