• 华为云·寻找黑马程序员#【代码重构之路】如何“消除”if/else【华为云技术分享】


    1. 背景

    if/else是高级编程语言中最基础的功能,虽然 if/else 是必须的,但滥用 if/else,特别是各种大量的if/else嵌套,会对代码的可读性、可维护性造成很大伤害,对于阅读代码的人来说就是一场灾难。

    本系列博客的目的不是消除if/else,而是如何“写好”if/else

    2. 方法

    根据if/else的使用方式和场景,大概有如下解决方法

    多态
    表驱动
    职责链模式
    卫语句
    Optional
    调整判断逻辑,抽取方法,逻辑优化

    本篇博客我介绍的是表驱动,后续博客会介绍其他案例
    首先来看下最简单的if…else if…场景,也是经常可以在代码中看到的案例

    3. 案例

    有如下业务代码demo,根据type值,判断然后返回设备的名称

     1 //逻辑表达模式固定的 if…else
     2 public String getDeviceName(int type){
     3     if (type == 1) {
     4         return "ONT";
     5     } else if (type == 2) {
     6         return "OLT";
     7     } else if (type == 3) {
     8         return "ONU";
     9     } else if (type == 4) {
    10         return "MXU";
    11     }
    12     return null;
    13 }

    随着时间的积累和项目的迭代,可能会增加越来越多的类型,那么后人会继续增加if/else分支,代码中存在的分支判断就会越来越多,当分支数量实在是多的难以维护的时候,我们就要考虑下,有办法能让这些代码变得更优雅吗?

    可能有的人会说用switch/case来重构代码

     1 public String getDeviceName(int type) {
     2     switch (type) {
     3         case 1:
     4             return "ONT";
     5         case 2:
     6             return "OLT";
     7         case 3:
     8             return "ONU";
     9         case 4:
    10             return "MXU";
    11         default:
    12             return null;
    13     }
    14 }

    可以看到,换成switch/case也是一样,后续也要维护大量case分支,特别是当同样的逻辑判断出现在多个地方的时候,代码的可读性和维护难易程度将变得非常的糟糕。每次修改时,你必须找到所有有逻辑分支的地方,并修改它们

    下面,我就来介绍一种针对这种if/else判断的最简单的重构方式,那就是使用表驱动

    4. 表驱动重构代码

    表驱动方法(Table-Driven Methods),《代码大全》对此进行了详细地讲解。

    表驱动法是一种编程模式(Scheme),从表里面查找信息而不使用逻辑语句(if 和case) 它的好处是消除代码里面到处出现的if、else、switch语句,让凌乱代码变得简明和清晰。
    对简单情况而言,表驱动方法可能仅仅使逻辑语句更容易和直白,但随着逻辑的越来越复杂,表驱动法就愈发有吸引力。

    1 static Map<Integer, String> deviceType2NameMap = new HashMap<>();
    2 static {
    3     deviceType2NameMap.put(1, "ONT");
    4     deviceType2NameMap.put(2, "OLT");
    5     deviceType2NameMap.put(3, "ONU");
    6     deviceType2NameMap.put(4, "MXU");
    7 }

    static方式可以在类初始化时就加载,当然,如果不想写成static,也可以自行加载

    那么,查询的时候,直接get就可以了,而且不需要对key值进行额外的判空

    String deviceName = deviceType2NameMap.get(type);

    当然,还有一种逻辑固定的if/else也很常见

    1 if ("run".equals(action)) {
    2    doRun(param);
    3 } else if ("fly".equals(action)) {
    4    doFly(param);
    5 } else if ("sleep".equals(action)) {
    6    doSleep(param);
    7 } // ....

    这里分支后的执行过程换成了函数,不同的行为执行不同的函数
    转换为表驱动方式如下

     1 public class Test {
     2 
     3 // 假定上述的param类型为int
     4 Map<String, Consumer<Integer>> actionMappings = new HashMap<>();
     5 initActionMap(){
     6     // 使用方法引用替换Lambda表达式
     7     // Test::doRun等价于param -> doRun(param)
     8     actionMappings.put("run", Test::doRun);
     9     actionMappings.put("fly", Test::doFly);
    10     actionMappings.put("sleep", Test::doSleep);
    11 }
    12 
    13 doRun(int param) {...}
    14 doFly(int param) {...}
    15 doSleep(int param) {...}
    16 // ....
    17 }

    调用方式如下

    actionMappings.get("run").accept(param);

    可能有人不清楚Consumer是个什么,为什么最后又执行了accept方法
    这里简单说下

    Consumer<T>是Java8以后提供的函数式接口
    T:入参类型;没有出参
    调用方法:void accept(T t);
    因为没有出参,常用于打印、发送短信等消费动作

    由此可见,表驱动的优势

    数据逻辑分离,保证在修改数据时,不会对逻辑产生影响。
    单元测试时可以注入表格,只要数据可以转换成表,我们可以输入任意形式的数据。
    逻辑固定写死在程序中,因为修改逻辑成本高,数据则是灵活变换的,因为修改数据成本低。
    保证多人开发时代码的稳健性,简单的逻辑易于读懂易于维护,并且多人使用时,只用修改数据段即可,而数据本身不需要再测试。


    来源:华为云社区征文 作者:EmindCC

    HDC.Cloud 华为开发者大会2020 即将于2020年2月11日-12日在深圳举办,是一线开发者学习实践鲲鹏通用计算、昇腾AI计算、数据库、区块链、云原生、5G等ICT开放能力的最佳舞台。

    欢迎报名参会

  • 相关阅读:
    ObjectARX代码片段二
    外部程序通过COM启动AutoCAD时RPC_E_CALL_REJECTED的问题解决办法
    ObjectARX代码片段一
    Sublime Text 3 修改插件安装位置【sublime text、插件路径、Data】
    Bug的处理
    界面测试的方法要点
    并发用户数、吞吐量、思考时间的计算公式
    常用测试工具下载
    SVN安装配置详解
    Loadrunner录制脚本时选择协议
  • 原文地址:https://www.cnblogs.com/huaweicloud/p/12016831.html
Copyright © 2020-2023  润新知