装饰者模式定义:动态的将新功能附加到对象上。在对象功能扩展方面,它比继承更有弹性,装饰者模式也体现了开闭原则(OCP)
装饰者模式类图:
角色:
1、抽象构件(Component):给出一个抽象接口,以规范准备接收附加责任的对象
2、具体构件(ConcreteComponent):定义一个将要接收附加责任的类
3、装饰(Decorator):持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口
4、具体装饰(ConcreteDecorator):负责给构件对象贴上附加的责任
应用举例:用装饰者模式解决咖啡订单,咖啡有Espresso、LongBlack、ShortBlack三种,每种咖啡都可以加巧克力、牛奶、豆浆三种调味品,根据不同的咖啡和咖啡添加的调味品计算订单价格
代码示例:
@Data
public abstract class Drink {
public String des;
private Double price = 0.0;
/**
* 计算费用的抽象方法
* @return 费用
*/
public abstract Double cost();
}
public class Coffee extends Drink {
@Override
public Double cost() {
return super.getPrice();
}
}
public class Espresso extends Coffee {
public Espresso(){
setDes("意大利咖啡");
//意大利咖啡6元
setPrice(6.0);
}
}
public class LongBlack extends Coffee {
public LongBlack(){
setDes("long black");
//long black价格5元
setPrice(5.0);
}
}
public class ShortBlack extends Coffee {
public ShortBlack(){
setDes("short black");
//short black价格4元
setPrice(4.0);
}
}
public class Decorator extends Drink {
private Drink drink;
public Decorator(Drink drink){
this.drink = drink;
}
@Override
public Double cost() {
return super.getPrice() + drink.cost();
}
@Override
public String getDes(){
//输出装饰者信息
return super.des + " " + super.getPrice() + "&&" + drink.getDes();
}
}
public class Chocolate extends Decorator {
public Chocolate(Drink drink) {
super(drink);
setDes("巧克力");
setPrice(3.0);
}
}
public class Milk extends Decorator {
public Milk(Drink drink) {
super(drink);
setDes("牛奶");
setPrice(2.0);
}
}
public class Soy extends Decorator {
public Soy(Drink drink) {
super(drink);
setDes("豆浆");
setPrice(1.5);
}
}
public class CoffeeBar {
public static void main(String[] args) {
//点一份long black
Drink longBlack = new LongBlack();
System.out.println(longBlack.getDes() + "费用:" + longBlack.cost());
//加一点牛奶
longBlack = new Milk(longBlack);
System.out.println(longBlack.getDes() + "费用:" + longBlack.cost());
//再加一份巧克力
longBlack = new Chocolate(longBlack);
System.out.println(longBlack.getDes() + "费用:" + longBlack.cost());
//再加一份巧克力
longBlack = new Chocolate(longBlack);
System.out.println(longBlack.getDes() + "费用:" + longBlack.cost());
}
}
运行结果:
long black费用:5.0
牛奶 2.0&&long black费用:7.0
巧克力 3.0&&牛奶 2.0&&long black费用:10.0
巧克力 3.0&&巧克力 3.0&&牛奶 2.0&&long black费用:13.0
装饰者模式的优缺点:
1、优点
- 比继承更灵活。
从为对象添加功能的角度看,装饰者模式比继承模式更为灵活。继承是静态的,一旦继承所有的子类都有一样的功能。装饰者模式采用把功能分离到每个装饰器当中,通过对象组合的方式,在运行时动态地组合功能,被装饰对象最终有哪些功能是由运行时动态组合的功能决定的。
- 复用功能更容易
装饰模式把一系列复杂的功能分散到每个装饰器中,一般情况下每个装饰器只实现一个功能,使得实现装饰器变得简单,有利于装饰器功能的复用,可以给一个对象添加 多个装饰器,也可以把一个装饰器装饰多个对象,从而实现复用装饰器的功能。
- 简化高层定义
装饰者模式可以通过组合装饰器的方式,为对象添加任意多的功能;因此在高层定义的时候,不必把所有的功能都定义处理,只需要定义最基本的就可以了,在需要的时候可以通过组合装饰器的方式来完成所需的功能。
2、缺点
装饰模式把一系列复杂的功能分散到每个装饰器中,一般情况下每个装饰器只实现一个功能,这样会产生很多细粒度的对象,并且功能越复杂,细粒度对象越多。
3、使用场合
- 如果需要再不影响其他对象的情况下,以动态、透明的方式给对象增加职责,可以使用装饰者模式。
- 如果不适合使用子类进行扩展的时候,可以考虑使用装饰者模式。装饰者模式使用的是对象组合的方式。
- 不适合子类扩展:比如扩展功能需要的子类太多,造成子类数量呈爆炸性增长。
注意:装饰者模式只是改变组件对象的外观Facde,并没有改变其内核