一、什么是装饰器设计模式

  装饰器模式(Decorator Pattern),是在不必改变原类文件和使用继承的情况下,动态的扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。 使用装饰器模式的时候需要注意一下几点内容:

  • 装饰对象和真实对象有相同的接口。这样客户端对象就可以以和真实对象相同的方式和装饰对象交互。
  • 装饰对象包含一个真实对象的引用。
  • 装饰对象接受所有的来自客户端的请求,它把这些请求转发给真实的对象。
  • 装饰对象可以在转发这些请求以前或以后增加一些附加功能。这样就确保了在运行时,不用修改给定对象的结构就可以在外部增加附加的功能。在面向对象的设计中,通常是通过继承来实现对给定类的功能扩展。然而,装饰者模式,可以在应用程序运行时,动态扩展功能,更加方便、灵活。 适用装饰者模式场。
  • 在我们想给一个类扩展多种功能时,可以使用装饰者设计模式进行多层装饰,这样我们可以把具体的扩展功能分开,如过再需要扩展功能时,只需要再增加装饰类即可,只需要在客户端调用时使用就好,减少了代码的耦合性,增加了可扩展性。

二、装饰器设计模式结构图

设计模式之 ==> 装饰器设计模式

  • Component定义的是一个接口,可以给这些对象动态的添加功能
  • ConcreteComponent是一个具体的类,是 Component 接口的实现类,我们主要是为了给这些具体的实现类来扩展一些功能
  • Decorator是装饰抽象类,继承 Component 接口,扩展 Component 的功能,但是对于 Component 来说,无需知道 Decorator 的存在
  • ConcteteDecorator是具体的装饰类,起到给ConcreteComponent类扩展功能的的作用

上图是装饰器模式的完整结构,但实际运用当中有很多可以变化的地方,如:

  • Component 可以是接口也可以是抽象类,甚至是一个普通的父类(这个强烈不推荐,普通的类作为继承体系的超级父类不易于维护)
  • 装饰器的抽象父类 Decorator 并不是必须的,这时,装饰器模式和静态代理模式非常的相似,区别就在于装饰器模式在装饰类中将被装饰的类当参数传入,需要装饰哪个类由客户端调用来决定,而静态代理则是在代理类中就已经把被代理的类明确了。

三、装饰器模式写法举例

下面,我们装饰器设计模式来实现一个功能:从数据库查询出一段内容,然后替换两处指定内容

首先,是待装饰的抽象类,相当于 Component

public abstract class AbstractProcessor<T, R> {

  public abstract RetMsg<R> doProcessor(T t);

}

然后是它的一个子类,相当于实现类 ConcreteComponent

public class MockProcessor extends AbstractProcessor<MockRequestDTO, String> {

  @Override
  public RetMsg<String> doProcessor(MockRequestDTO mockRequestDTO) {

    String mockData = getMockDataFromDB(mockRequestDTO).getData();
    return RetMsg.buildSuccessMsg(mockData);
  }

  private RetMsg<String> getMockDataFromDB(MockRequestDTO mockRequestDTO){
    
    // 从数据库中去查数据
    Set<MappingConfDO> mappingConfs = DaoFacade.of().mapping(MappingConfMapper.class,
            mapper -> mapper.queryByUrlAndHost(mockRequestDTO.getMockUrl(), mockRequestDTO.getRemoutHost()));

    if (mappingConfs.isEmpty()) {
      return RetMsg.buildFailedMsg("没有找到匹配数据");
    }

    if (mappingConfs.size() > 1) {
      return RetMsg.buildFailedMsg("找到的匹配数据太多");
    }

    // 这是找到了我们所要的那个匹配,并将MappingConfDO转化为MappingConfDTO
    MappingConfDTO mappingConfDTO = mappingConfs.stream().map(MappingConfDO::toMappingConfDTO).findFirst().get();

    // 从 response_data 表查出数据,返回的是ResponseDataDO对象
    ResponseDataDO responseDataDO = DaoFacade.of().mapping(ResponseDataMapper.class,
            mapper -> mapper.quaryByMappingId(mappingConfDTO.getMappingId()));

    return RetMsg.buildSuccessMsg(responseDataDO.getData());
  }
}

以上 MockProcessor 类的 doProcessor() 方法是从数据库中查询匹配的数据。假设我们查出来的数据的格式:AAA &{date:yyyy-MM-dd HH:mm:ss} BBB &{id:16},我们需要对这段数据中的 date 和 id 后面的字符串根据一定规则进行替换。

接下来抽象装饰父类,相当于 Decorator

设计模式之 ==> 装饰器设计模式

public class AbstractDecorator extends AbstractProcessor<MockRequestDTO,String> {

  protected AbstractProcessor<MockRequestDTO,String> processor;

  public AbstractDecorator(AbstractProcessor<MockRequestDTO, String> processor) {
    this.processor = processor;
  }

  @Override
  public RetMsg<String> doProcessor(MockRequestDTO mockRequestDTO) {
    return processor.doProcessor(mockRequestDTO);
  }
}

AbstractDecorator