基本介绍

相信通过名称大家可以大致猜出“装饰者模式”的含义,它的核心思想就是通过一些“装饰品(类)”对原有的基础类进行包装,从而增强或者是完成某些特定的功能。

装饰者模式”可以动态地将新功能附加到对象上。并且在对象的扩展方面,使用该模式要比单纯的通过继承更加有弹性。

核心设计UML类图:

程序员理解设计模式(程序员必备技能)(1)

通过上面的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()); } }

执行结果:

程序员理解设计模式(程序员必备技能)(2)

以上就是该案例的所有代码,可能一开始看这个代码的时候不是特别理解。不要慌,容我给大家稍加讲解,相信各位就能一目了然,明白其中的奥秘。

其实该案例运用了递归算法的思想,我们可以拆解一下。

第一步:客户下单,要了一份奶茶。第二步:客户在奶茶的基础上,点了一个燕麦。此时我们需要装饰一下第一步中的奶茶(将其视为一个整体),从而得到一个加了燕麦的奶茶。第三步:客户在加了燕麦的奶茶基础上,点了一个珍珠。此时我们需要装饰的就是第二步中加了燕麦的奶茶了(将其视为一个整体)。自此整个订单完成,得到一个加了燕麦和珍珠的奶茶(装饰完成)。

示例图:

程序员理解设计模式(程序员必备技能)(3)

这样解释是不是就清楚地明白其中的奥秘了。

总结

1、使用装饰者模式可以更加灵活的对程序进行扩展,可随着业务的需要动态的新增功能,也可在不需要时进行撤回,并且通过不同的装饰者和被装饰者可以衍生出不同的组合。

2、使用装饰者模式对程序进行扩展是符合ocp原则的。

3、使用装饰者模式也会增加代码的复杂性,并且当使用过度时会衍生出过多小的类,从而增加系统的维护成本。

任何设计模式都是有其优点和缺点的,我们要做到在合适的场景合理的使用设计模式。

今天的分享就到这里了,如果感觉“菜鸟”写的文章还不错,记得点赞加关注呦!你们的支持就是我坚持下去的动力。文章哪里写的有问题的也希望大家可以指出,我会虚心受教。

程序员理解设计模式(程序员必备技能)(4)

,