4.1、介绍
定义:装饰器模式(Decorator)是指动态地将新功能附加到对象上。在功能扩展方面,它比继承更有弹性,装饰着模式也体现了开闭原则(OCP)。
举例:现有一家咖啡店,售卖的咖啡有ShortBlack、Espresso、LongBlack等,另有调料Chocolate、Milk、Soy等,购买规则是任选一种咖啡,可搭配一种或多种调料,问怎么设计系统得出顾客购买的咖啡详情和价格:顾客要购买2份Chocolate+1份Milk的LongBlack。
这种类型的设计模式属于结构型模式。
4.2、代码支撑
先有抽象类 Drink.java
,里面有字段 describe
、price
以及它们的 set/get()
方法,并且有费用计算的抽象方法:
public abstract class Drink {
private String describe;
private float price;
public String getDescribe() {
return describe;
}
public void setDescribe(String describe) {
this.describe = describe;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
public abstract float cost();
}
再用类 Coffee.java
继承 抽象类 Drink.java
。( Coffee.java
类是被装饰者):
public class Coffee extends Drink {
@Override
public float cost() {
return super.getPrice();
}
}
咖啡目前有三种(ShortBlack、Espresso、LongBlack),都继承类 Coffee.java
,重写 describe
、price
字段:
第一种咖啡的信息和价格 ShortBlack.java
public class ShortBlack extends Coffee {
public ShortBlack() {
setDescribe("ShortBlack");
setPrice(6.0f);
}
}
第二种咖啡的信息和价格 Espresso.java
public class Espresso extends Coffee {
public Espresso() {
setDescribe("Espresso");
setPrice(5.0f);
}
}
第三种咖啡的信息和价格 LongBlack.java
public class LongBlack extends Coffee {
public LongBlack() {
setDescribe("LongBlack");
setPrice(4.0f);
}
}
下面写装饰者 Decorator.java
, 一样 继承 Drink.java
,重写添加了调料的咖啡费用和信息:
public class Decorator extends Drink {
private Drink drinkObj;// 被装饰者,即咖啡
public Decorator(Drink drinkObj) {
this.drinkObj = drinkObj;
}
@Override
public float cost() {
// 调料自己的价格 + 被装饰者的价格
return super.getPrice() + drinkObj.cost();
}
@Override
public String getDescribe() {
// 调料信息 + 调料价格 + 被装饰者信息
return super.getDescribe() + "(¥" + super.getPrice() + ")、" + drinkObj.getDescribe();
}
}
调料具体有Chocolate、Milk、Soy等,都继承装饰者 Decorator.java
,并创建构造器:
调料Chocolate作为装饰者 Chocolate.java
:
public class Chocolate extends Decorator {
public Chocolate(Drink drinkObj) {
super(drinkObj);
setDescribe("Chocolate");
setPrice(6.6f);
}
}
调料Milk作为装饰者 Milk.java
:
public class Milk extends Decorator{
public Milk(Drink drinkObj) {
super(drinkObj);
setDescribe("Milk");
setPrice(5.5f);
}
}
调料Soy作为装饰者 Soy.java
:
public class Soy extends Decorator{
public Soy(Drink drinkObj) {
super(drinkObj);
setDescribe("Soy");
setPrice(4.4f);
}
}
现在被装饰者(咖啡)和装饰者(调料)都搭建完毕,下面是客户端 Client.java
点餐:
public class Client {
public static void main(String[] args) {
// 订单:2份Chocolate+1份Milk的LongBlack
// 选择咖啡LongBlack
Drink order = new LongBlack();
System.out.println("需支付总额:¥" + order.cost() + " ,购买信息:" + order.getDescribe());
//添加调料1份Milk
order = new Milk(order);
System.out.println("需支付总额:¥" + order.cost() + " ,购买信息:" + order.getDescribe());
// 添加2份Chocolate
order = new Chocolate(order);
System.out.println("需支付总额:¥" + order.cost() + " ,购买信息:" + order.getDescribe());
order = new Chocolate(order);
System.out.println("需支付总额:¥" + order.cost() + " ,购买信息:" + order.getDescribe());
}
}
控制台输出结果:
如果店铺进货了其他咖啡,那么只需要给该咖啡创建一个类,这个类继承 Coffee.java
,且生成构造器设置该咖啡的信息和价格。
4.3、总结
装饰器模式,是一种在运行期动态给某个对象的实例增加功能的方法。
就增加功能来说,相比生成子类更为灵活。但它有许多子类,过度使用会增加程序得复杂性。
装饰器模式的目的,就是把一个一个的附加功能,用Decorator的方式给一层一层地累加到原始数据源上,最终,通过组合获得我们想要的功能。