写点什么

重构大面积 if-else 代码

作者:廊虞
  • 2024-03-20
    广东
  • 本文字数:3018 字

    阅读完需:约 10 分钟

重构大面积if-else代码

知识点

  • 代码整洁之道

  • 设计模式之工厂模式、策略模式

问题描述

在大多小公司,没有较好的业务规划和技术架构,随着业务迭代、需求变更,会积累大量的条件判断逻辑。比如一个接口在不同 App 版本执行不同业务、对不同接口参数进行不同解析统计、对接 N 家 M 个商家收款账号等等。都会造成大量的 if-else 代码堆积,维护时影响心情,特别是让“我”维护。


++如何优化如下代码++:


    private void handle01(HttpServletRequest request) { ... }    private void handle02(HttpServletRequest request) { ... }    private void handle03(HttpServletRequest request) { ... }    private void handle04(HttpServletRequest request) { ... }    private void handle05(HttpServletRequest request) { ... }    private void handleDefault(HttpServletRequest request) { ... }        public void demo(String uri, HttpServletRequest request) {        if("/a/user".equals(uri)){            handle01(request);        } else if("/b/document_list".equals(uri)){            handle02(request);        } else if("/b/content_list".equals(uri)){            handle03(request);        } else if("/b/ext_list".equals(uri)){            handle04(request);        } else if("/b/xxx".equals(uri)){            handle05(request);        } else {            handleDefault(request);        }    }
复制代码

方案

认识策略模式

策略模式指:某个行为在不同的场景中,需要使用不同的算法。我们将不同的算法称之为策略,在不同的环境中,获取不同的策略进行执行。


如题:我们会处理每个请求,根据每个请求的 uri 不同,采用不同的算法。这个行为就是接口请求,不同场景就是不同的 uri。所以我们可以使用策略模式去优化 if-else 代码。

封装处理函数

  1. 定义策略接口,让所有策略都通过 exec 方法执行算法。


public interface Strategy{    void exec(HttpServletRequest request);}
复制代码


  1. 定义各个算法实现类,实现 Strategy 接口 exec 方法。


public class H1 implements Strategy{    @Override    public void exec(HttpServletRequest request) { }}public class H2 implements Strategy{    @Override    public void exec(HttpServletRequest request) { }}
复制代码


  1. 这样那些逻辑类,就可以修改为如下方式,只判断使用哪个策略,具体执行算法,在每个实现类中实现,整个类的作用也更加明确。


public void demo(String uri, HttpServletRequest request) {    Strategy strategy = null;    if("/a/user".equals(uri)){        strategy = new H1();    } else if("/b/document_list".equals(uri)){        strategy = new H2();    } else if("/b/content_list".equals(uri)){        strategy = new H3();    } else if("/b/ext_list".equals(uri)){        strategy = new H4();    }    if (strategy != null){        strategy.exec(request);    }}
复制代码


  1. 可结合工厂模式获取策略对象。

项目应用

假设需求为 ==/ly/mysql、/ly/redis、/ly/es== 3 个接口需要将请求日志实时存储到==mysql、redis、elasticsearch== 3 种不同数据库中。


  1. 定义一个注解,作为实现类的标识


/** 定义注解 */@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Uri {    String[] value();}
复制代码


  1. 定义接口和实现类。为了方便工厂类的读取,建议实现类放到同一个包中(如:com.example.strategy.impl)。工厂类会在初始化时通过反射创建对象。


/** 策略接口 */public interface Strategy{    void write(HttpservletRequest rquest);}
/** MySql实现 */@Uri("/ly/mysql")public class MySqlStrategy implements Strategy{ public void write(HttpservletRequest request){ log.info("写入mysql成功"); }}
/** Redis实现 */@Uri("/ly/redis")public class RedisStrategy implements Strategy{ public void write(HttpservletRequest request){ log.info("写入Redis成功") }}
/** Es实现 */@Uri("/ly/es")public class ESStrategy implements Strategy{ public void write(HttpservletRequest request){ log.info("写入ES成功") }}
复制代码


  1. 定义工厂类,在项目启动时,初始化工厂类。为每个 uri 创建对应的策略对象,方便使用。


public class StrategyFactory{    private ApiBuryPointFactory(){}        //保存每个uri的策略对象,临时实现,如果策略存在并发问题,这样存储不可行    private static final Map<String,Strategy> StrategyMap = new ConcurrentHashMap<>();        /** 获取uri策略对象 */    public static Strategy getStrategy(String uri){        if(uri == null){            return null;        }        return StrategyMap.get(uri);    }        //初始化    static {init();}        /** 初始化,获取包 com.example.strategy.impl包下的class文件,创建对象 */    @SneakyThrows    private static void init() {        final PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();        final Resource[] resources = resolver.getResources("classpath:com/example/strategy/impl/*.class");        if(resources.length == 0){            return;        }        for (Resource resource : resources) {            final String className = new SimpleMetadataReaderFactory().getMetadataReader(resource).getClassMetadata().getClassName();            final Class<?> clazz = Class.forName(className);            if(!clazz.isAnnotationPresent(Uri.class)){                continue;            }            //获取注解            final Uri annotation = clazz.getAnnotation(Uri.class);            final String[] uris = annotation.value();            if(uris == null || uris.length < 1){                continue;            }            final Object instance = clazz.newInstance();            if (!(instance instanceof ApiBuryPointTactics)) {                continue;            }            for (String uri : uris) {                TacticsMAP.put(uri, (ApiBuryPointTactics) instance);            }            log.info("url={},策略={}", uris, className);        }    }}
复制代码


  1. 这样我们就可以在拦截器或者过滤器中通过 StrategyFactory.getStrategy(String)方法获取策略对象并执行 write()方法。

  2. uri 相关基本都通过正则匹配,可在工厂方法中通过正则、类正则方法匹配 uri,获取策略对象。


    // 使用AntPathMatcher匹配uri    public static Strategy getStrategy(String uri){        if(uri == null){            return null;        }        final AntPathMatcher matcher = new AntPathMatcher();        for (Map.Entry<String, Strategy> entry : StrategyMap.entrySet()) {            if(matcher.match(entry.getKey(), uri)){                return entry.getValue();            }        }        return null;    }
复制代码


发布于: 刚刚阅读数: 4
用户头像

廊虞

关注

还未添加个人签名 2018-07-25 加入

还未添加个人简介

评论

发布
暂无评论
重构大面积if-else代码_Java_廊虞_InfoQ写作社区