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


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

      装饰器模式(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 在前,结果都是正常的。

  • 相关阅读:
    bootstrap 弹出框(Popover)插件 修改title等属性选项值
    dedecms 搬家流程
    jQuery ui 百叶窗blind方向设置
    css 优先级
    dedecms 标签
    dedecms 建站相关问题
    css 透明度使用
    css 边框使用
    css 阴影使用
    js 常用判断
  • 原文地址:https://www.cnblogs.com/L-Test/p/13726977.html
Copyright © 2020-2023  润新知