基本介绍
相信通过名称大家可以大致猜出“装饰者模式”的含义,它的核心思想就是通过一些“装饰品(类)”对原有的基础类进行包装,从而增强或者是完成某些特定的功能。
“装饰者模式”可以动态地将新功能附加到对象上。并且在对象的扩展方面,使用该模式要比单纯的通过继承更加有弹性。
核心设计UML类图:
通过上面的UML类图,可以得知装饰者模式大致可以分为四个角色:
Component(抽象组件): 可以是一个抽象类或者接口,是要被包装类的顶级类。
ConcreteComponent(具体组件): Component的子类,是具体被包装的类。
注:有的时候会存在很多ConcreteComponent类,为了方便统一管理我们可以根据不同ConcreteComponent的特性在抽象出不同父类,将其放置在上图Component和ConcreteComponent之间,也就是在这两者之间设计一个缓冲层。
Decorator(装饰者): 该类继承或实现Component,并在类中聚合一个Component类的对象。
ConcreteDecorator(具体装饰者): Decorator的子类,是具体的装饰者类,该类的作用就是装饰ConcreteComponent类。
简单案例
通过上面的类图和文字描述相信大家对“装饰者模式”应该有了一些基本的认识,下面让我们通过一个小案例来加深对该设计模式的理解。
案例:有一个饮品店,该店有不同口味的饮品和不同种类的配料。现在需要一个下单系统,要求该系统可以计算出客户通过自由搭配而得的饮品价格。
饮料 => Component(抽象组件)
public abstract class Drink {
// 描述
private String describe;
// 价格
private double price = 0.0;
// 计算费用的抽象方法
public abstract double cost();
// get()和set()
}
奶茶 => ConcreteComponent(具体组件)
public class TeaWithMilk extends Drink {
public TeaWithMilk() {
setDescribe("奶茶");
setPrice(6.0);
}
@Override
public double cost() {
// 当前奶茶的价格
return getPrice();
}
}
调味品 => Decorator(装饰者)
public class Seasoning extends Drink {
// 聚合一个Drink对象,也就是被装饰的对象。
private Drink drink;
// 构造
public Seasoning(Drink drink) {
this.drink = drink;
}
@Override
public double cost() {
// 当前调味品的价格加上被装饰对象的价格。
return getPrice() + drink.cost();
}
/**
* 重写getDescribe方法,方便输出打印
*/
@Override
public String getDescribe() {
return super.getDescribe() + " " + getPrice() + " + " + drink.getDescribe();
}
}
ConcreteDecorator(具体装饰者)
/**
* 珍珠
*/
public class Pearl extends Seasoning {
// 构造
public Pearl(Drink drink) {
super(drink);
setDescribe("珍珠");
setPrice(2.5);
}
}
/**
* 燕麦
*/
public class Oats extends Seasoning {
// 构造方法
public Oats(Drink drink) {
super(drink);
setDescribe("燕麦");
setPrice(1.0);
}
}
客户端测试类
public class Client {
// 订单:一杯奶茶 + 一份燕麦 + 一份珍珠
public static void main(String[] args) {
// 一杯奶茶
Drink drink = new TeaWithMilk();
System.out.println("需支付:");
System.out.println(drink.getDescribe() + drink.cost());
// 加一份燕麦
drink = new Oats(drink);
System.out.println("加一份燕麦需支付:");
System.out.println(drink.getDescribe() + " 总共:" + drink.cost());
drink = new Pearl(drink);
System.out.println("再加一份珍珠需支付:");
System.out.println(drink.getDescribe() + " 总共:" + drink.cost());
}
}
执行结果:
以上就是该案例的所有代码,可能一开始看这个代码的时候不是特别理解。不要慌,容我给大家稍加讲解,相信各位就能一目了然,明白其中的奥秘。
其实该案例运用了递归算法的思想,我们可以拆解一下。
第一步:客户下单,要了一份奶茶。
第二步:客户在奶茶的基础上,点了一个燕麦。此时我们需要装饰一下第一步中的奶茶(将其视为一个整体),从而得到一个加了燕麦的奶茶。
第三步:客户在加了燕麦的奶茶基础上,点了一个珍珠。此时我们需要装饰的就是第二步中加了燕麦的奶茶了(将其视为一个整体)。自此整个订单完成,得到一个加了燕麦和珍珠的奶茶(装饰完成)。
示例图:
这样解释是不是就清楚地明白其中的奥秘了。
总结
1、使用装饰者模式可以更加灵活的对程序进行扩展,可随着业务的需要动态的新增功能,也可在不需要时进行撤回,并且通过不同的装饰者和被装饰者可以衍生出不同的组合。
2、使用装饰者模式对程序进行扩展是符合ocp原则的。
3、使用装饰者模式也会增加代码的复杂性,并且当使用过度时会衍生出过多小的类,从而增加系统的维护成本。
任何设计模式都是有其优点和缺点的,我们要做到在合适的场景合理的使用设计模式。
今天的分享就到这里了,如果感觉“菜鸟”写的文章还不错,记得点赞加关注呦!你们的支持就是我坚持下去的动力。文章哪里写的有问题的也希望大家可以指出,我会虚心受教。