• 项目代码 if/else 过多,引起程序猿口吐莲花


    一、吐槽模式

    项目开发周期短,迭代速度快,时间久了之后,代码中可能会充斥着大量的if/else,一层一层的嵌套,让人短时间内不可能领悟该段代码的逻辑,久而久之,后面接手的人可能真的会口吐莲花!

    //就像这样,一步一步变成金字塔
    if (condition1) {
        action1();
        if (condition2) {
            action2();
            if (condition3) {
                action3();
                if (condition4) {
                    action4();
                }
            }
        }
    }
    

    每当我们接手这样的一个老项目,可能会默默的开启吐槽模式

    1.这项目怎么感觉毫无设计,想啥做啥,太坑了吧
    2.这尼玛全是逻辑判断,也没注释,看 NM 啊
    3.逻辑嵌套这么多,看死我了,谁写的垃圾代码?
    4.我先忍忍吧,不行,我也跑路了...

    程序猿何苦为难程序猿呢,谁不想做个好猿,你看老板给你机会吗,“三天时间,不能再多了,周一上线”

    其他

    那么过多的 if...else ,我们该如何去优化呢,让我们的程序看起来更加清爽一点?也为项目的后续接手人,减轻点负担,少吐槽一点。

    二、解决方式

    提前返回

    说白了就是判断条件取反,让程序不满足条件是提前返回,代码在逻辑表达上会更清晰,看下面代码:

    if (condition) {
       // do something
    } else {
      return xxx;
    }
    
    //换成先判断!condition,去掉else
    if (!condition) {
      return xxx;
     
    } 
    // do something
    

    Optional

    Optional主要用于非空判断,是jdk8中新的特性,所以使用的不是特别多。

    //如果登录用户为空,执行action1,否则执行action 2
    //用之前
    if (user == null) {
        //do action 1
    } else {
        //do action2
    }
    //用之后
    Optional<User> userOptional = Optional.ofNullable(user);
    userOptional.map(action1).orElse(action2);
    
    //使用Optional优化之后,让非空校验更加优雅,间接的减少if操作
    

    Assert

    Assert可以帮助我们实现各种参数验证,比如字符串不为空等。还有其他很多框架类库,例如 Spring、Apache Commons 都提供了工具类,用于实现这种通用的功能,这样大家就不必自行编写 if...else 了。
    例如:

    Assert.hasText(param.getType(), "参数类型不能为空");
    

    关于Spring 的 Assert 类,可以查阅:https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/util/Assert.html

    表驱动法

    逻辑表达模式固定的 if/else代码,可以通过映射关系,将逻辑表达式用表格的方式表示,再使用表格查找的方式,找到某个输入所对应的处理函数,使用这个处理函数进行运算。
    例如:

    if (param.equals(value1)) {
        doAction1(someParams);
    } else if (param.equals(value2)) {
        doAction2(someParams);
    } else if (param.equals(value3)) {
        doAction3(someParams);
    }
    

    采用表驱动法重构后

    Map<?, Function<?> action> actionMappings = new HashMap<>();
    
    //初始化
    actionMappings.put(value1, (someParams) -> { doAction1(someParams)});
    actionMappings.put(value2, (someParams) -> { doAction2(someParams)});
    actionMappings.put(value3, (someParams) -> { doAction3(someParams)});
    
    // 省略 null 判断
    actionMappings.get(param).apply(someParams);
    

    表的映射关系,可以采用集中的方式,也可以采用分散的方式,即每个处理类自行注册,也可以通过配置文件的方式。

    事件驱动

    通过关联不同的事件类型和对应的处理机制,来实现复杂的逻辑,同时达到解耦的目的。从理论角度讲,事件驱动可以看做是表驱动的一种,但从实践角度讲,事件驱动和前面提到的表驱动有多处不同。主要表现为:

    表驱动通常是一对一的关系;事件驱动通常是一对多;

    表驱动中,触发和执行通常是强依赖;事件驱动中,触发和执行是弱依赖

    正是上述两者不同,导致两者适用场景的不同,具体来说,事件驱动可用于如订单支付完成触发库存、物流、积分等功能。

    注解驱动

    通过 Java 注解(或其它语言的类似机制)定义执行某个方法的条件。在程序执行时,通过对比入参与注解中定义的条件是否匹配,再决定是否调用此方法。注解驱动,很多框架中都能看到这种模式的使用,比如我们经常用的 Spring中,具体实例,大家可以参考:https://www.cnblogs.com/houzheng/p/10911462.html

    策略模式

    针对以下四种策略的场景,我们采用两种优化方案来举例

    if (strategy.equals("fast")) {
      //do FAST action
    } else if (strategy.equals("normal")) {
      //do NORMAL action
    } else if (strategy.equals("smooth")) {
      //do SMOOTH action
    } else if (strategy.equals("slow")) {
      //do SLOW action
    }
    

    多态

    interface Strategy {
      void run() throws Exception;
    }
    
    class FastStrategy implements Strategy {
        @Override
        void run() throws Exception {
            //do FAST action
        }
    }
    
    class NormalStrategy implements Strategy {
        @Override
        void run() throws Exception {
             //do NORMAL action
        }
    }
    
    class SmoothStrategy implements Strategy {
        @Override
        void run() throws Exception {
            //do SMOOTH action
        }
    }
    
    class SlowStrategy implements Strategy {
        @Override
        void run() throws Exception {
            //do SLOW action
        }
    }
    
    //调用伪代码(此处可以map对象来保存策略,或者反射调用,或者交给spring获取bean等方式很多)
    Strategy strategy = map.get(param);
    strategy.run();
    

    枚举

    在枚举中定义方法,分解逻辑调用

    public enum Strategy {
        FAST {
            @Override
            void run() {
                //do FAST action
            }
        },
        NORMAL {
            @Override
            void run() {
                //do NORMAL action
            }
        },
    
        SMOOTH {
            @Override
            void run() {
                //do SMOOTH action
            }
        },
    
        SLOW {
            @Override
            void run() {
                //do SLOW action
            }
        };
    
        abstract void run();
    }
    
    //调用伪代码
    Strategy strategy = Strategy.valueOf(param);
    strategy.run();
    

    职责链模式

    当 if...else 中的条件表达式灵活多变,无法将条件中的数据抽象为表格并用统一的方式进行判断时,这时应将对条件的判断权交给每个功能组件。并用链的形式将这些组件串联起来,形成完整的功能,适用于条件表达式灵活多变,没有统一形式的场景。

    实现与示例

    //重构前
    public void handle(request) {
        if (handlerA.canHandle(request)) {
            handlerA.handleRequest(request);
        } else if (handlerB.canHandle(request)) {
            handlerB.handleRequest(request);
        } else if (handlerC.canHandle(request)) {
            handlerC.handleRequest(request);
        }
    }
    //重构后
    public void handle(request) {
      handlerA.handleRequest(request);
    }
    
    public abstract class Handler {
      protected Handler next;
      public abstract void handleRequest(Request request);
      public void setNext(Handler next) { this.next = next; }
    }
    
    public class HandlerA extends Handler {
      public void handleRequest(Request request) {
        if (canHandle(request)) doHandle(request);
        else if (next != null) next.handleRequest(request);
      }
    }
    

    三、各抒己见

    道友A:策略带来更多的代码量,逻辑也不是一目了然,还不如 if/else,老夫就喜欢if/else,哈哈哈
    道友B:我不多写点if/else,不让代码看起来绕绕的,怎么体现我的工作量
    道友C:我都写了这么多代码了,你才告诉我可以这么优化,我太累了
    道友D:为啥不用switch,switch比if/else 看起来舒服多了
    道友E:工作太忙,加班太多,拒绝了发言...

    .

    其他

  • 相关阅读:
    PCA,到底在做什么
    论文笔记:Deep feature learning with relative distance comparison for person re-identification
    论文笔记:Cross-Domain Visual Matching via Generalized Similarity Measure and Feature Learning
    word2vec概述
    登录获取token,token参数关联至所有请求的请求体内
    pip安装库时报错,使用国内镜像加速
    python+unittest+requests+HTMLRunner编写接口自动化测试集
    python实现http get请求
    python实现以application/json格式为请求体的http post请求
    反编译apk
  • 原文地址:https://www.cnblogs.com/jstarseven/p/14389363.html
Copyright © 2020-2023  润新知