美文网首页
装饰器模式在项目中的实际运用

装饰器模式在项目中的实际运用

作者: 小胖学编程 | 来源:发表于2020-11-04 18:21 被阅读0次

1. 理论

装饰器(Decorator)模式:允许向一个现有的对象添加新的功能,同时又不改变器其结构。

这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供额外的功能。

意图:Decorator模式采用对象组合而非继承的方式,实现了动态的扩展对象功能的能力。而且可以根据需要扩展多个功能,避免了单独使用继承带来的“灵活性差”和“大量子类”的问题。

使用场景:在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。处理那些可以撤销的职责。

  1. 当不能采用生成子类的方式进行扩展时。一种情况是:可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。
  2. (重写源码)因为类定义被隐藏,或类定义不能用于生成子类。
结构图.png

代理模式和装饰器模式的区别

在装饰器模式中,必须有被装饰的类和装饰的类。代理模式一定是自身持有这个对象,不需要从外部传入。而装饰模式一定是从外部传入(即代理模式的目标对象不对外,代理对象全权处理;装饰器模式是辅助,目标仍然可以对外提供服务,装饰器模式只是增强作用),并且可以没有顺序,按照代码的试卷需要随意挑换顺序。

从使用上,代理模式注重隔离限制,让外部不能方法实际调用对象,比如权限控制,装饰模式注重的是功能的扩展,在一个方法下实现更多的功能。

2. 项目实际用法

接口类型(Component):给出一个抽象类接口,以规范准备接收附加责任的对象

/**
 * 具体的业务接口
 */
public interface MappingBusinessService {

    /**
     * 接口的方法
     */
    ItemAnswerResp assembleItemAndAnswer(OriResult oriResult, PlanInfoDto planInfoDto);
}

具体子类(ConcreteCompoent):定义一个业务接口的具体实现:

而实际业务中,可以多种设计模式组合使用,此处使用“模板方法模式”来实现。

/**
 * 模板方式——将大逻辑拆分为小逻辑。
 */
@Slf4j
public abstract class AbstractMappingBusinessService implements MappingBusinessService {
    @Override
    public ItemAnswerResp assembleItemAndAnswer(OriResult oriResult, PlanInfoDto planInfoDto) {
        //数据校验
        validateHandler(planInfoDto);
        //数据准备
        prepareHandler(planInfoDto);
        //获取做的记录
        List<ItemAnswerDto> itemAnswerInfo = getItemAnswerInfo(planInfoDto);
        log.info(JSON.toJSONString(itemAnswerInfo));
        return null;
    }

    /**
     * 子类必须实现的抽象方法
     */
    public abstract List<ItemAnswerDto> getItemAnswerInfo(PlanInfoDto planInfoDto);

    /**
     * 校验器
     */
    protected void validateHandler(PlanInfoDto planInfoDto){
    }

    /**
     * 数据准备器
     */
    protected void prepareHandler(PlanInfoDto planInfoDto){
        log.info("AbstractMappingBusinessService的数据准备");
    }

}
/**
 * 二级父类
 */
@Slf4j
public abstract class AbstractSimpleMappingBusinessService extends AbstractMappingBusinessService {

    /**
     * 某些业务场景下,可能存在多级父类情况
     */
    protected void prepareHandler(PlanInfoDto planInfoDto) {
        super.prepareHandler(planInfoDto);
        log.info("AbstractSimpleMappingBusinessService的数据准备");
    }

}

具体的子类,子类已经在Spring容器中。

/**
 * 具体子类
 */
@Service
@Slf4j
public class MappingBusinessServiceImpl3 extends AbstractSimpleMappingBusinessService {
    @Override
    public List<ItemAnswerDto> getItemAnswerInfo(PlanInfoDto planInfoDto) {
        ItemAnswerDto itemAnswerDto = new ItemAnswerDto();
        itemAnswerDto.setName("亚瑟");
        ItemAnswerDto itemAnswerDto2 = new ItemAnswerDto();
        itemAnswerDto2.setName("韩信");
        return Arrays.asList(itemAnswerDto, itemAnswerDto2);
    }
}

装饰器类

装饰器类和具体实现继承的是同一接口。

/**
 * 装饰器
 */
@Slf4j
public class LogMappingBusinessDecorator implements MappingBusinessService {

    private MappingBusinessService mappingBusinessService;

    public LogMappingBusinessDecorator(MappingBusinessService mappingBusinessService) {
        this.mappingBusinessService = mappingBusinessService;
    }

    @Override
    public ItemAnswerResp assembleItemAndAnswer(OriResult oriResult, PlanInfoDto planInfoDto) {
        log.info("日志装饰器增加可以被撤销的职责");
        return mappingBusinessService.assembleItemAndAnswer(oriResult, planInfoDto);
    }
}

疑问?装饰器类能实现抽象类接口吗?

是可以的。装饰器类可以实现抽象类。来动态的扩展抽象类。

但是实现的抽象父类必须重写所有的非private方法。

/**
 * 实现抽象类接口的装饰器对象
 */
@Slf4j
public class MappingValidateDecorator  extends AbstractMappingBusinessService{

    private AbstractMappingBusinessService abstractMappingBusinessService;

    public MappingValidateDecorator(AbstractMappingBusinessService abstractMappingBusinessService) {
        this.abstractMappingBusinessService = abstractMappingBusinessService;
    }

    @Override
    public List<ItemAnswerDto> getItemAnswerInfo(PlanInfoDto planInfoDto) {
        return abstractMappingBusinessService.getItemAnswerInfo(planInfoDto);
    }

    /**
     * 虽然该装饰器不是处理"数据准备处理器",但是必须原样返回子类的配置(除private方法)。
     * 否则子类将继承抽象类的配置。这可能会导致问题。
     *
     */
    @Override
    protected void prepareHandler(PlanInfoDto planInfoDto) {
        abstractMappingBusinessService.prepareHandler(planInfoDto);
    }

    @Override
    protected void validateHandler(PlanInfoDto planInfoDto) {
        abstractMappingBusinessService.validateHandler(planInfoDto);
        log.info("---->校验处理器:MappingValidateDecorator");
    }
}

(核心)装饰后放入到Spring容器中

可以灵活的被多个装饰器装饰,动态的扩展功能。

/**
 * 放入到Spring容器中
 */
@Configuration
public class MappingConfig {
    @Bean("mappingDecorator1")
    public MappingBusinessService getV1(MappingBusinessServiceImpl1 mappingBusinessServiceImpl1){
        return new MappingDecorator(mappingBusinessServiceImpl1);
    }
    @Bean("mappingValidateDecorator2")
    public MappingBusinessService getV2(MappingBusinessServiceImpl2 mappingBusinessServiceImpl2){
        return new MappingValidateDecorator(mappingBusinessServiceImpl2);
    }
   
    @Bean("mappingValidateDecorator3")
    public MappingBusinessService getV3(MappingBusinessServiceImpl3 mappingBusinessServiceImpl3){
        return new LogMappingBusinessDecorator(new MappingValidateDecorator(mappingBusinessServiceImpl3));
    }
}

(核心)路由枚举

动态的获取Spring对象。
注:创建的是装饰器对象。

@Getter
public enum MappingEnum {
    V1(1, "mappingDecorator1"),
    V2(2, "mappingValidateDecorator2"),
    V3(3, "mappingValidateDecorator3");

    private Integer type;

    private String clazz;

    MappingEnum(Integer type, String clazz) {
        this.type = type;
        this.clazz = clazz;
    }

    public static MappingEnum resolve(Integer type) {
        for (MappingEnum mappingEnum : values()) {
            if (mappingEnum.type.equals(type)) {
                return mappingEnum;
            }
        }
        throw new RuntimeException("类型不存在");
    }

    public static MappingBusinessService getBean(Integer type) {
        return SpringUtils.getBean(resolve(type).clazz, MappingBusinessService.class);
    }
}

借助的工具类。

@Component
public class SpringUtils implements ApplicationContextAware {
    private static ApplicationContext applicationContext;

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        if (SpringUtils.applicationContext == null) {
            SpringUtils.applicationContext = applicationContext;
        }
    }

    public static Object getBean(String name) {
        return getApplicationContext().getBean(name);
    }

    public static <T> T getBean(Class<T> c) {
        return getApplicationContext().getBean(c);
    }

    public static <T> T getBean(String name, Class<T> c) {
        return getApplicationContext().getBean(name, c);
    }
}

测试

@RestController
public class TestMappingController {

    @PostMapping("test1")
    public void test(@RequestParam("type") Integer type){
        MappingBusinessService bean = MappingEnum.getBean(type);
        ItemAnswerResp itemAnswerResp = bean.assembleItemAndAnswer(null, null);
    }
}

效果:

(LogMappingBusinessDecorator.java:25): 日志装饰器增加可以被撤销的职责
(MappingValidateDecorator.java:41): ---->校验处理器:MappingValidateDecorator
(AbstractMappingBusinessService.java:44): AbstractMappingBusinessService的数据准备
(AbstractSimpleMappingBusinessService.java:20): AbstractSimpleMappingBusinessService的数据准备
(AbstractMappingBusinessService.java:25): [{"name":"亚瑟"},{"name":"韩信"}]

推荐阅读

http://m.elecfans.com/article/566111.html

相关文章

网友评论

      本文标题:装饰器模式在项目中的实际运用

      本文链接:https://www.haomeiwen.com/subject/ivbgvktx.html