什么是装饰者模式?
动态地将责任附加到对象上。若要扩展功能,装饰者提供了比继承更有弹性的代替方案。
案例(一点点奶茶店)
众所周知,一点点是一家很火的奶茶店。大街小巷都能看到分店。现在准备更新订单系统,以合乎他们的奶茶供应要求。
先看看他们原来的订单的设计
这只是列出一部分,而且奶茶的种类可以自定义,如果按照上面的订单设计,只要加一个种类,就要加一个类,而且当某一个佐料的价格改变时候,只要用过这个佐料的类都要重新修改,维护起来太麻烦了。
那么就用装饰者模式来解决这一难题。用装饰者模式的思想来思考奶茶店,就会是,要以奶茶作为主体,然后在运行时以调料来“装饰”奶茶。比如:顾客想要一杯加了奶霜和红豆的波霸奶茶,利用装饰者的思想做法将变成:
- 拿一个波霸奶茶
- 以奶霜装饰它
- 以红豆装饰它
-
调用cost()方法,并依赖委托将调料的价钱加上去
大致看上去是这个样子的:
在算钱的时候,顺序是从外向内的,先调用红豆奶霜波霸奶茶的cost(),而红豆奶霜波霸奶茶的cost()会调用奶霜波霸奶茶的cost(),而奶霜波霸奶茶的cost()会调用波霸奶茶的cost(),最后算价钱。听起来有点绕,简单想就好像链式编程一样,或者积攒耐心向后看。
在看案例的具体实现之前,再来看看装饰者模式的类图。
下面是一点点奶茶店利用装饰者模式的类图。
代码实现:
奶茶类
public abstract class Beverage {
//饮料的描述
String description = "Unknow Beverage";
//获得饮料的描述
public String getDescription() {
return description;
}
//饮料的价格
public abstract double cost();
}
调料Decorator类
/**
* 调料的抽象类,这个类是一个装饰类,并且属于Beverage类的一种
*/
public abstract class CondimentDecorator extends Beverage {
//所有的调料装饰者都必须重新实现getDescription方法
public abstract String getDescription();
}
具体的奶茶类
波霸奶茶:
public class BoBaMilkTea extends Beverage {
public BoBaMilkTea() {
description = "波霸奶茶";
}
@Override
public double cost() {
return 10;
}
}
四季青奶茶:
public class SiJiQingMilkTea extends Beverage {
public SiJiQingMilkTea() {
description = "四季青奶茶";
}
@Override
public double cost() {
return 12;
}
}
具体调料类
红豆:
public class RedBean extends CondimentDecorator {
//被装饰者
Beverage beverage;
//建立装饰者和被装饰者之间的联系
public RedBean(Beverage beverage) {
this.beverage = beverage;
}
//装饰者开始装饰啦,第一步是描述,装饰后的描述是饮料的描述+自己装饰者的描述;而其中饮料的描述是委托beverage去实现的
@Override
public String getDescription() {
return beverage.getDescription() + ", 红豆";
}
//装饰者开始装饰啦,第二部价格,同上
@Override
public double cost() {
return 2+beverage.cost();
}
}
奶霜
public class MilkCream extends CondimentDecorator {
Beverage beverage;
public MilkCream(Beverage beverage) {
this.beverage = beverage;
}
@Override
public String getDescription() {
return beverage.getDescription()+",奶霜";
}
@Override
public double cost() {
return beverage.cost() + 5;
}
}
开始下订单了:
public class ALittleMilkTeaStore {
public static void main(String[] args) {
//一杯波霸奶茶
Beverage milkTea = new BoBaMilkTea();
System.out.println(milkTea.getDescription() + "RMB" + milkTea.cost());
System.out.println("-----------------------------------------------");
//一杯红豆椰果波霸奶茶
Beverage milkTea2 = new BoBaMilkTea();
milkTea2 = new RedBean(milkTea2);
milkTea2 = new Coconut(milkTea2);
System.out.println(milkTea2.getDescription() + "RMB" + milkTea2.cost());
System.out.println("-----------------------------------------------");
//一杯四季青加两份奶霜
Beverage milkTea3 = new SiJiQingMilkTea();
milkTea3 = new MilkCream(milkTea3);
milkTea3 = new MilkCream(milkTea3);
System.out.println(milkTea3.getDescription() + "RMB" + milkTea3.cost());
}
}
结果为:
波霸奶茶RMB10.0
-----------------------------------------------
波霸奶茶,红豆,椰果RMB12.0
-----------------------------------------------
四季青奶茶,奶霜,奶霜RMB23.0
装饰者模式也算是比较常用的,比如JavaI/O 就用到了装饰者模式。
自我总结
装饰者和被装饰者有相同的超类型。还是拿奶茶的例子,比如红豆波霸奶茶,奶茶就是超类型,波霸奶茶就是一个具体的被装饰者且继承超类型,而红豆就是装饰者,并且也继承了超类型,虽然波霸奶茶和红豆都继承了奶茶这个超类型,但两种不同的含义。波霸奶茶的继承是要表达,“我就是奶茶的子类”;而红豆的继承是要表达,“我是为奶茶这个类服务的,我要装饰这个类”。这就是为什么装饰者和被装饰者有相同的超类型。