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

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

  装饰器模式(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

再来是具体的装饰类 DateTimeResponseDecorator 和 IdResponseDecorator,分别用于替换 AAA &{date:yyyy-MM-dd HH:mm:ss} BBB &{id:16} 中的 date 和 id 之后的内容。相当于 ConcteteDecorator

public class DateTimeResponseDecorator extends AbstractDecorator {

  private static Pattern DATETIME_PATTERN = Pattern.compile("&\\{date:(.*?)}");

  public DateTimeResponseDecorator(AbstractProcessor<MockRequestDTO, String> processor) {
    super(processor);
  }

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

    RetMsg<String> retMsg = this.processor.doProcessor(mockRequestDTO);

    if (retMsg.isSuccess()) {
      String data = retMsg.getData();

      // 替换代码
      Matcher matcher = DATETIME_PATTERN.matcher(data);
      while (matcher.find()) {
        String format = matcher.group(1);
        String currentTime = genCurrentTime(format);
        data = StringUtils.replace(data, genSearchElement(format), currentTime);
      }
      retMsg.setData(data);
      return retMsg;
    }

    return super.doProcessor(mockRequestDTO);
  }

  private String genSearchElement(String format) {
    return new Formatter().format("&{date:%s}", format).toString();
  }

  private String genCurrentTime(String format) {
    return LocalDateTime.now().format(DateTimeFormatter.ofPattern(format));
  }
}
DateTimeResponseDecorator
public class IdResponseDecorator extends AbstractDecorator {

  private static final Pattern ID_PATTERN = Pattern.compile("&\\{id:(.*?)}");

  public IdResponseDecorator(AbstractProcessor<MockRequestDTO, String> processor) {
    super(processor);
  }

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

    RetMsg<String> retMsg = this.processor.doProcessor(mockRequestDTO);

    if (retMsg.isSuccess()) {
      String data = retMsg.getData();

      // 替换代码
      Matcher matcher = ID_PATTERN.matcher(data);
      while (matcher.find()) {
        String format = matcher.group(1);
        String id = IdUtils.getId(Integer.parseInt(format));
        data = StringUtils.replace(data, genSearchElement(format), id);
      }
      retMsg.setData(data);
      return retMsg;
    }

    return super.doProcessor(mockRequestDTO);
  }

  private String genSearchElement(String format) {
    return new Formatter().format("&{id:%s}", format).toString();
  }
}
IdResponseDecorator

最后来看一下客户端调用

public final class CoreFacade {

  private CoreFacade() {
    //
  }
  public static String doMock(MockRequestDTO mockRequestDTO){
    // 这部分功能实现使用了装饰器设计模式,一层一层包下去
    RetMsg<String> retMsg = new IdResponseDecorator(new DateTimeResponseDecorator(new MockProcessor())).doProcessor(mockRequestDTO);
    return retMsg.isSuccess() ? retMsg.getData() : retMsg.getErrMsg();
  }
}

从客户端调用可以看出,装饰器设计模式有多个装饰类的时候,可以一层一层包下去,每一层的功能都是相对独立的,甚至包装的顺序都是可以变化的,我们只需要保证的是最里层的是我们的被装饰类,所以我们可以把客户端的调用写成这样:

RetMsg<String> retMsg = new DateTimeResponseDecorator(new IdResponseDecorator(new MockProcessor())).doProcessor(mockRequestDTO);

最后,我们来看下最终的运行结果:

无论装饰类 DateTimeResponseDecorator 在前还是 IdResponseDecorator 在前,结果都是正常的。

posted on 2020-09-25 12:27  破解孤独  阅读(542)  评论(0编辑  收藏  举报

导航