干掉了传统if-else的竟然是它!太长见识了![策略模式的具体实现]

普通的策略模式见文章:https://www.cnblogs.com/java-spring/p/12988778.html
下面是更加具体的特殊的策略模式的实现!
由于项目的需求,需要对接多家招采平台和多家保险公司,每次支付完成后,都要根据选择的不同的保险公司,做特殊的逻辑处理。一段时间就会增加一个来源,已经把之前的逻辑改的面目全。出于长远的考虑,我决定对现有的逻辑进行重构,毕竟长痛不如短痛。

传统的实现方式

我们看下边的伪代码,大致就是重构前逻辑的代码,由于来源比较少,简单的做if-else逻辑判断足以满足需求。
但每过一段时间,来源说不定就会增加,再用这种方式做已经无法维护了,想象一下那种臃肿的if-else代码,别说开发想想都头大!

public class OrderServiceImpl implements IOrderService {
    @Override
    public String handle(String companyId) {
        if ("A".equals(companyId)) {
            return "进行A保险公司投保业务";
        } else if ("B".equals(companyId)) {
            return "进行B保险公司投保业务";
        } else if ("C".equals(companyId)) {
            return "进行C保险公司投保业务";
        }
        return null;
    }
}

策略模式的实现方式

策略模式定义了一个拥有共同行为的算法族,每个算法都被封装起来,可以互相替换,独立于客户端而变化。

一、策略模式的使用场景:

针对同一问题的多种处理方式,仅仅是具体行为有差别时;需要安全地封装多种同一类型的操作时;同一抽象类有多个子类,而客户端需要使用if-else 或者 switch-case 来选择具体子类时。

每个保险公司来源都有自己单独的逻辑实现类,而每次需要添加保险公司来源,直接新建实现类,修改@HandlerType(“zhlh”)的数值即可,再也不用去翻又臭又长的if-lese。

不仅如此在分配任务时,每个人负责开发几种订单来源逻辑,都可以做到互不干扰,而且很大程度上减少了合并代码的冲突。

二、具体的实现过程:
自定义注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface HandlerType {
    String value() default "";
}
自启动包扫描类
@Component
public class MyHandler implements ApplicationRunner {
    //扫描到的类的容器
    private List<String> classNames=new ArrayList<String>();
    //存放扫描到被@HandlerType注解上的实现类
    private Map<String,Class> maps=new HashMap<>();

    @Override
    public void run(ApplicationArguments args) throws Exception {
        scanPackage("com.ftx.jwt");
        System.out.println(classNames);

        for(String className:classNames){
            //拿到类的全路径名,去掉后缀名
            String cn = className.replace(".class", "");
            Class<?> clazz = Class.forName(cn);
            if(clazz.isAnnotationPresent(HandlerType.class)){
                System.out.println(clazz.getName()+"实现类已被@HandlerType注解标注");
                HandlerType annotation = (HandlerType)clazz.getAnnotation(HandlerType.class);
                maps.put(annotation.value(),clazz);
            }
        }
        //注册到spring容器
        DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
        defaultListableBeanFactory.registerSingleton("BeanName",new HandlerContext(maps));

    }

    //扫描包
    public void scanPackage(String packageName){
        URL url = this.getClass().getClassLoader().getResource("" + packageName.replaceAll("\\.", "/"));
        System.out.println(url);
        //   file:/D:/IdeaProjects/springboot-myannotation/target/classes/com/ftx/myannotation
        String file = url.getFile();
        File files=new File(file);
        for(File file1:files.listFiles()){
            if(file1.isDirectory()){
                //递归扫描
                scanPackage(packageName+"."+file1.getName());
            }else{
                classNames.add(packageName+"."+file1.getName());
                //包名+类名 com.ftx.myannotation.controller.TestController
            }
        }
    }
}
存储实现类的容器类

此容器存储项目启动时把扫描到的加注解的实现类,并加入到spring容器中,根据类型type实例化抽象类

public class HandlerContext {
    private static Map<String, Class> handlerMap;

    public HandlerContext(Map<String, Class> handlerMap) {
        this.handlerMap = handlerMap;
    }

    public static AbstractHandler getInstance(HttpServletRequest request,String type) throws IllegalAccessException, InstantiationException {
        ServletContext servletContext = request.getSession().getServletContext();
        WebApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
        Class clazz = handlerMap.get(type);
        if (clazz == null) {
            throw new IllegalArgumentException("没有找到@HandlerType的value值为 " + type+" 的实现类");
        }
        AbstractHandler abstractHandler = (AbstractHandler) clazz.newInstance();
        return abstractHandler;
    }
}
向上抽象出来一个具体的业务处理器
public abstract class AbstractHandler {
    /**
     * 功能描述:投保
     */
    abstract public String assemblyData();
}
实现抽象类的两个实现类
@HandlerType(value = "one")
public class AbstractHandlerImpl1 extends AbstractHandler {
    @Override
    public String assemblyData() {
        return "我是标注为 one 的实现类的方法返回值";
    }
}
@HandlerType(value = "two")
public class AbstractHandlerImpl2 extends AbstractHandler {
    @Override
    public String assemblyData() {
        return "我是标注为 two 的实现类的方法返回值";
    }
}
调用入口
@GetMapping("/test")
    public void test(HttpServletRequest request,String type) throws InstantiationException, IllegalAccessException {
        AbstractHandler one = HandlerContext.getInstance(request, type);
        String s = one.assemblyData();
        System.out.println("执行的是被@HandlerType标注且value值为"+ type +"的实现类的assemblyData方法");
        System.out.println(s);
    }

以上设计模式方式看着略显复杂,很些小伙伴提出质疑:“你为了个if-else,弄的如此的麻烦,又是自定义注解,又弄这么多类不麻烦吗?” 还有一些小伙伴纠结于性能问题,策略模式的性能可能确实不如if-else。
但我觉得吧增加一点复杂度、牺牲一丢丢性能,换代码的整洁和可维护性还是值得的。不过,一个人一个想法,怎么选还是看具体业务场景吧!

策略模式的优缺点
优点

易于扩展,增加一个新的策略只需要添加一个具体的策略类即可,基本不需要改变原有的代码,符合开放封闭原则避免使用多重条件选择语句,充分体现面向对象设计思想 策略类之间可以自由切换,由于策略类都实现同一个接口,所以使它们之间可以自由切换每个策略类使用一个策略类,符合单一职责原则 客户端与策略算法解耦,两者都依赖于抽象策略接口,符合依赖反转原则客户端不需要知道都有哪些策略类,符合最小知识原则

缺点

策略模式,当策略算法太多时,会造成很多的策略类客户端不知道有哪些策略类,不能决定使用哪个策略类,这点可以通过封装common公共包解决,也可以考虑使IOC容器和依赖注入的方式来解决。

总结

凡事都有他的两面性,if-else多层嵌套和也都有其各自的优缺点:

if-else的优点就是简单,想快速迭代功能,逻辑嵌套少且不会持续增加,if-else更好些,缺点也是显而易见,代码臃肿繁琐不便于维护。
策略模式 将各个场景的逻辑剥离出来维护,同一抽象类有多个子类,需要使用if-else 或者 switch-case 来选择具体子类时,建议选策略模式,他的缺点就是会产生比较多的策略类文件。

两种实现方式各有利弊,如何选择还是要依据具体业务场景,还是那句话设计模式不是为了用而用,一定要用在最合适的位置。

注:以上代码只是策略模式的一种实现方法,策略模式属于设计模式的一种,设计模式(Design Pattern)是前辈们对代码开发经验的总结,是解决特定问题的一系列套路。它不是语法规定,而是一套用来提高代码可复用性、可维护性、可读性、稳健性以及安全性的解决方案。

本文参考自:https://juejin.im/post/6844904185377325064

posted @ 2021-01-14 12:47  修电脑的  阅读(7)  评论(0)    收藏  举报