利用策略模式彻底消除if-else

背景

项目一开始需要针对两个功能添加终止功能,因为只涉及两个功能,所以就偷懒直接用if-else模式处理了,心想着以后有机会在重构吧,没想到来的这么突然,又需要对几个功能点添加终止功能,就利用这个契机索性对代码进行重构了。

解决

由于项目原因,需求我们用(利用不同code值选择不同音频设备处理实际需求)代替。
话不多说,我们直接勺代码。

项目结构图

利用策略模式彻底消除if-else_第1张图片

各类代码
  1. TypeEnum枚举

    public enum TypeEnum {
    
        MP3(0,"我是mp3"),
        MP4(1,"我是mp4"),
        MP5(2,"我是mp5"),
        ;
    
        private int code;
        private String msg;
    
        TypeEnum(int code, String msg) {
            this.code = code;
            this.msg = msg;
        }
    
        public int getCode() {
            return code;
        }
    
        public String getMsg() {
            return msg;
        }
    }
    
  2. StrategyTypeHandler注解

    @Documented
    @Inherited
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface StrategyTypeHandler {
        TypeEnum value();
    }
    
  3. Strategy接口

    /**
     * 策略接口
    **/
    public interface Strategy {
    
        /**
         * 每个策略执行的方法
         **/
        String custom(String param);
    }
    
  4. XxxStrategy类(具体执行策略的类,均类似所以只写一个例子)

    /**
     * @ClassName Mp3Strategy
     * @Description 如果类型为mp3时需执行的方法
     **/
    @Component
    @StrategyTypeHandler(TypeEnum.MP3) //标注每个类所支持的类型
    public class Mp3Strategy implements Strategy {
        @Override
        public String custom(String param) {
            System.out.println("我是处理MP3格式的方法");
            //todo 书写自己的业务处理逻辑
            return "MP3:" + param;
        }
    }
    
  5. StrategyContext类

    /**
     * @ClassName StrategyContext
     * @Description 策略容器上下文
     **/
    @Component
    public class StrategyContext {
        private final Map handlerMap = new HashMap<>();
    
        public Strategy getStrategy(Integer type) {
            return handlerMap.get(type);
        }
    
        public void putStrategy(Integer code, Strategy strategy) {
            handlerMap.put(code, strategy);
        }
    }
    
  6. StrategyListener类

    /**
     * @ClassName StrategyListener
     * @Description 服务启动后初始化策略容器
     **/
    @Component
    public class StrategyListener implements ApplicationListener {
    
        @Override
        public void onApplicationEvent(ContextRefreshedEvent event) {
            Map beans = event.getApplicationContext().getBeansWithAnnotation(StrategyTypeHandler.class);
            StrategyContext strategyContext = event.getApplicationContext().getBean(StrategyContext.class);
            beans.forEach((name, bean) -> {
                StrategyTypeHandler typeHandler = bean.getClass().getAnnotation(StrategyTypeHandler.class);
                strategyContext.putStrategy(typeHandler.value().getCode(), (Strategy) bean);
            });
        }
    }
    
  7. TestStrategyController模拟调用类

    /**
     * @ClassName TestStrategyController
     * @Description 模拟测试策略模式控制器
     **/
    @RestController
    @RequestMapping("strategy")
    public class TestStrategyController {
    
        @Resource
        private StrategyContext strategyContext;
    
        /**
         * 根据不同的code值选则不同的处理模式来处理
         * code为0时选择mp3处理,为1时选择mp4处理,为2时选择mp5处理
        **/
        @GetMapping("test")
        public String testStrategy(@RequestParam(value = "code") Integer code,
                                   @RequestParam(value = "param") String param) {
            Strategy strategy = strategyContext.getStrategy(code);
            if (strategy == null) {
                return "不支持的格式";
            }
            return strategy.custom(param);
        }
    
    }
    
  8. 到这里代码架构就可以了,我们可以通过自己写的 Controller 类传入不同的 code 进行测试。

扩展

当我们改成完全策略模式后,任你类别千千万,我们都可以以不变应万变。当需要支持新的类别时(例如:Mp8/Mp6),我们只需要改动两个地方:

  1. 在TypeEnum枚举中添加新支持格式的枚举
  2. 添加一个Xxx类实现Strategy接口,并在类上加上StrategyTypeHandler标注其格式类型

小结

相信看完了上面代码的例子,即使你对策略模式理论还不能完全理解,但是也足够解决项目中类似的实际问题了。
以上代码源码地址:Git-Hub:strategy-demo

你可能感兴趣的:(学习,Java8)