前言
装饰者模式:动态地将责任附加到对象上,若要扩展功能,装饰者提供了比继承更具有弹性的替代方案。
场景
我们知道,咖啡店里面的口味特别多,有dark roast(深烘焙),也有Espresso(浓缩咖啡),也有Machiatto(马琪雅朵)等等。顾客点咖啡的时候也会顺便点些配料,如牛奶(milK),摩卡(Mocha),配料多达几十种。对于这种场景,如果让我们实现一个订单系统,每一个订单显示顾客点了哪些东西和价格,该如何实现呢?
第一想法
用一个咖啡基类Beverage计算添加调料的总价格,然后各个口味的咖啡类继承该基本类,覆盖cost()方法,并返回父类的调料价格和该口味的价格。
ublic class Beverage {
public String description; //描述订单定了哪些东西
public double cost(){
double money = 0;
if (hasMilk()){
money += 1.0; //加牛奶配料的价格
}
if (hasMilk()){
money += 2.0; //摩卡的价格
}
...
}
public boolean hasMilk(){
...
}
public boolean hasMocha(){
...
}
....
}
当顾客点了DarkRost咖啡时:
public class DarkRoast extends Beverage {
public DarkRoast(){
description = "Most excellent dark roast";
}
@Override
public double cost() {
return super.cost() + 2.0; //2.0为单点DarkRoast的价格
}
...
}
这样的订单系统,当调料和口味品种比较少时,是满足需求的。但是当调料多时,父类Beverage中判断该订单是否有某个调料的语句就越来越多,而且有些调料在某些口味中是不可能搭配的确被继承到该口味中。更重要的是,当新增调料的时候,需要在父类添加该调料,这就违反了开发的对扩展开发,对修改封闭的原则。这时候装饰模式就正好解决该问题。
装饰设计模式
当应用装饰设计模式时,当顾客点了一杯DarkRoast,可直接计算出该对象的价格。顾客觉得加点牛奶会更好喝,所以点了一些牛奶加在咖啡上,这时我们只需对DarkRoast对象粉饰一番,将这个对象加上Milk生成新的粉饰对象,新的对象加上milk的价格。最后顾客又点了摩卡,我们对这个新的粉饰对象再进行一番装饰,加上摩卡的价格。这样,这个最终的对象是由DarkRoast不断的被添加新的装饰点形成的。顾客没有要求Soy调料,Soy调料就不会作为装饰点去粉饰DarkRoast对象。总体思路是需要什么,就添加什么,扩展什么。一个对象可以被多个对象进行粉饰包装。用图形象的表示如下:
为了实现这种效果,粉饰对象和被粉饰对象应该同属一个类型,即被粉饰对象DarkRoast和粉饰对象Milk、Mocha继承同一个类。粉饰对象可以获取被粉饰对象的行为并添加上自己的行为。装饰者设计模式总体设计框架如下图所示:
按照这个框架图,来实现我们的代码。先创建一个基类Beverage,它是DarkRoast口味和调料Milk、Mocha共同继承的对象。
public abstract class Beverage {
public String description;
public String getDescription(){
return description;
}
public abstract double cost();
}
然后DarkRoast继承Beverage,它是具体被粉饰的对象。
public class DarkRoast extends Beverage {
public DarkRoast(){
description = "Most excellent dark roast";
}
@Override
public double cost(){
return 1.0;
}
}
再创建粉饰对象共同的接口,这个接口也要继承Beverage,以实现粉饰对象和被粉饰对象同属一个类型。
public abstract class CodimentDecorator extends Beverage {
public abstract String getDescription();
}
粉饰对象共有的接口有了之后,就可以实现具体的实现类了。
public class Milk extends CodimentDecorator {
private Beverage beverage;
public Milk(Beverage beverage){
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription()+", Milk";
}
@Override
public double cost() {
return beverage.cost()+2.0;
}
}
public class Mocha extends CodimentDecorator {
private Beverage beverage;
public Mocha(Beverage beverage){
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription()+" , Mocha";
}
@Override
public double cost() {
return 6.0 + beverage.cost();
}
}
每一个粉饰类都有一个Beverage对象,用来传入被粉饰的对象,并对被粉饰的对象加上自己的行为。
最后我们来调试一下:
public class Main {
public static void main(String[] args){
Beverage darkRoast = new DarkRoast(); //我点了一杯DarkRoast
System.out.println(darkRoast.getDescription()+" "+darkRoast.cost());
darkRoast = new Milk(darkRoast); //老板再来点牛奶
System.out.println(darkRoast.getDescription()+" "+darkRoast.cost());
darkRoast = new Mocha(darkRoast); //老板加点Mocha
System.out.println(darkRoast.getDescription()+" "+darkRoast.cost());
}
}
最终结果为:
Most excellent dark roast 1.0
Most excellent dark roast, Milk 3.0
Most excellent dark roast, Milk , Mocha 9.0