动态地将责任附加到对象上。想要扩展功能,装饰着提供有别于继承的另一种选择。
1.使用装饰者模式,可以允许行为被扩展,而无须修改现有的代码。
2.装饰者模式意味着一群装饰者类,这些类用来包装具体组件。
3.装饰者类反映出被装饰的组件类型(事实上,它们具有相同的类型,都经过接口或者继承实现)。
4.装饰者可以在被装饰者的行为前面或后面加上自己的行为,甚至将被装饰者的行为整个取代掉,而达到特定的目的。
5.装饰者会导致设计中出现许多小对象,如果过度使用,会让程序变得很复杂。
需求:为某奶茶店设计一个订单系统,客户可根据向奶茶中添加不同的材料来收取费用。
最开始的设计图如下:
但是人们在购买奶茶的使用,往往需要加入一些原料,例如:红豆,珍珠,布丁等,根据加入调料的不同,收取的费用不一样,如果根据奶茶加原料组合来创建每一个类,那样会需要创建太多的类来满足需求了。
另一种思路就是把需要的原料加到基类当中。
但是这样设计出现的问题就是,当调料的价格改变时,需要改变现在的代码,一旦出现新的调料,我们就需要加上新的方法,还要改变cost()方法。这是就体现了一个重要的设计原则:类应该对扩展开放,对修改关闭。我们的目标是允许类容易扩展,再不修改现有代码的情况下,就可搭配新的行为。这样的设计更具有弹性可以应对改变,可以接受新的功能来应对改变的需求。
现在以奶茶为例,来说明如何使用装饰者模式:当顾客需要一杯珍珠奶茶加布丁加红豆时:
1.拿一个珍珠奶茶作为对象。
2.以布丁对象来装饰它。
3.以红豆来装饰它。
4.最后调用cost()方法,将总价格计算出来。
其模式就如下所示:一层套一层
使用装饰者对象的前提是:装饰者和被装饰者对象有相同的超类型。
我们的设计类图如下:
1.编写抽象类
public abstract class MilkTea {
String description = "UnKnown MilkTea";
public String getDescription() {
return description;
}
public abstract double cost();
}
2.具体奶茶
/**
* 布丁奶茶,实现奶茶类
*/
public class BoudinMilkTea extends MilkTea {
public BoudinMilkTea() {
description = "boudinMilkTea";
}
@Override
public double cost() {
return 11.0;
}
}
/**
* 珍珠奶茶,实现奶茶抽象类
*/
public class PearlMilkTea extends MilkTea {
public PearlMilkTea() {
description = "PearlMilkTea";
}
@Override
public double cost() {
return 10.5;
}
}
3.创建装饰者的抽象类
/**
* 装饰者类
*/
public abstract class CondimentDecorator extends MilkTea {
public abstract String getDescription();
}
4.创建具体的装饰者
/**
* 红豆对象,是一个装饰者,所以扩展CondimentDecorator
*/
public class RedBeen extends CondimentDecorator {
//用一个实例变量来记录奶茶,也就是被装饰者
MilkTea milkTea;
public RedBeen(MilkTea milkTea) {
this.milkTea = milkTea;
}
@Override
public String getDescription() {
return milkTea.getDescription() + ", RedBeen";
}
@Override
public double cost() {
return 2 + milkTea.cost();
}
}
public class Pudding extends CondimentDecorator {
MilkTea milkTea;
public Pudding(MilkTea milkTea) {
this.milkTea = milkTea;
}
@Override
public String getDescription() {
return milkTea.getDescription() + ", Pudding";
}
@Override
public double cost() {
return 2.5 + milkTea.cost();
}
}
5.编写测试类:
public class TestMilkTea {
public static void main(String[] args) {
//单点一杯珍珠奶茶
MilkTea milkTea = new PearlMilkTea();
System.out.println(milkTea.getDescription() + " ¥" + milkTea.cost());
//点一杯奶茶加料
MilkTea milkTea1 = new BoudinMilkTea();
milkTea1 = new RedBeen(milkTea1);
milkTea1 = new Pudding(milkTea1);
System.out.println(milkTea1.getDescription() + " ¥" +milkTea1.cost());
}
}
6.测试结果:
PearlMilkTea ¥10.5
boudinMilkTea, RedBeen, Pudding ¥15.5