优秀程序员必备技能:设计模式之——装饰者模式

基本介绍

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

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

核心设计UML类图:

在这里插入图片描述

通过上面的UML类图,可以得知装饰者模式大致可以分为四个角色:

Component(抽象组件): 可以是一个抽象类或者接口,是要被包装类的顶级类。

ConcreteComponent(具体组件): Component的子类,是具体被包装的类。

注:有的时候会存在很多ConcreteComponent类,为了方便统一管理我们可以根据不同ConcreteComponent的特性在抽象出不同父类,将其放置在上图Component和ConcreteComponent之间,也就是在这两者之间设计一个缓冲层。

Decorator(装饰者): 该类继承或实现Component,并在类中聚合一个Component类的对象。

ConcreteDecorator(具体装饰者): Decorator的子类,是具体的装饰者类,该类的作用就是装饰ConcreteComponent类。

简单案例

通过上面的类图和文字描述相信大家对“装饰者模式”应该有了一些基本的认识,下面让我们通过一个小案例来加深对该设计模式的理解。

扫描二维码关注公众号,回复: 11619517 查看本文章

案例:有一个饮品店,该店有不同口味的饮品和不同种类的配料。现在需要一个下单系统,要求该系统可以计算出客户通过自由搭配而得的饮品价格。

饮料 => 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、使用装饰者模式也会增加代码的复杂性,并且当使用过度时会衍生出过多小的类,从而增加系统的维护成本。

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

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

猜你喜欢

转载自blog.csdn.net/first_M/article/details/108329401