阅读更多

11顶
1踩

编程语言
引用

原文: Anti-If: The missing patterns
作者: Joe Wright
翻译: 孙薇

大约十年前,我听说了反if的活动,觉得这个概念非常荒谬。如果不用if语句,又怎么能写出有用的程序呢?这简直太荒谬了。

但之后你会开始思考:是否还记得上周你拼命想读懂的深度嵌套代码?糟透了对么?要是有办法能简化它该多好。

反if活动的网站上没给出多少实用性建议,因此在本文中,作者将会提供一系列模式,也许你会用得上。但首先我们来关注一下if语句到底造成了什么问题。

if语句的问题
if语句的第一个问题在于,通常出现if语句的代码很容易越改越糟。我们试着写个新的if语句:
public void theProblem(boolean someCondition) {
        // SharedState

        if(someCondition) {
            // CodeBlockA
        } else {
            // CodeBlockB
        }
}

这时候还不算太糟,但已经存在一些问题了。在阅读这段代码时,我必须得去查看对同一个SharedState来说,CodeBlockA和CodeBlockB有什么改动。最开始这段代码很容易阅读,但随着CodeBlock越来越多,耦合越来越复杂之后,就会很难读。

上面这种CodeBlock进一步嵌套if语句与本地return的滥用情况也很常见,很难搞懂业务逻辑是选择了哪种路径。

if语句的第二个问题在于:复制时会有问题,也就是说,if语句缺失domain的概念。很容易由于在不需要的情况下,由于将内容放在一起而增加耦合性,造成代码难读难改。

而第三个问题在于:开发者必须在头脑中模拟执行实现情况——你得让自己变成一台小型电脑,从而造成脑细胞浪费。开发者的精力应当用来思考如何解决问题,而不是浪费在如何将复杂的代码分支结构编织在一起之上。

虽然想要直截了当地写出替代方案,但首先我得强调这句话:

凡事中庸而行,尤其是中庸本身

if语句通常会让代码更加复杂,但这不代表我们要完全抛弃if语句。我曾经看到过一些非常糟糕的代码,只是为了消除所有的if语句而刻意避开if语句。我们想要绕开这个误区,
下面我给出的每种模式,都会给出使用范围。

单独的if语句如果不复制到其他地方,也许是不错的句子。在复制if语句时,我们会希望预知危险的第六感起效。

在代码库之外,在与危险的外部世界交流时,我们会想要验证incoming response,并根据其作出相应的修改。但在自己的代码库中,由于有可靠的gatekeeper把关,我觉得这是个很好的机会,我们可以尝试使用简单、更为丰富与强大的替代方案来实现。

模式1:布尔参数(Boolean Params)
背景: 有方法在修改行为时使用了boolean。
public void example() {
    FileUtils.createFile("name.txt", "file contents", false);
    FileUtils.createFile("name_temp.txt", "file contents", true);
}

public class FileUtils {
    public static void createFile(String name, String contents, boolean temporary) {
        if(temporary) {
            // save temp file
        } else {
            // save permanent file
        }
    }
}

问题: 在看到这段代码时,实际上你是将两个方法捆绑到一起,布尔参数的出现让你有机会在代码中定义一个概念。

适用范围: 通常看到这种情况,如果在编译时我们可以算出代码要采用哪种路径,就可以放心使用这种模式。

解决方案: 将这个方法拆分成两个新的方法,然后if就不见了。
public void example() {
    FileUtils.createFile("name.txt", "file contents");
    FileUtils.createTemporaryFile("name_temp.txt", "file contents");
}

public class FileUtils {
    public static void createFile(String name, String contents) {
        // save permanent file
    }

    public static void createTemporaryFile(String name, String contents) {
        // save temp file
    }
}

模式2:使用多态(Polymorphism)
背景: 根据类型switch时。
public class Bird {

    private enum Species {
        EUROPEAN, AFRICAN, NORWEGIAN_BLUE;
    }

    private boolean isNailed;
    private Species type;

    public double getSpeed() {
        switch (type) {
            case EUROPEAN:
                return getBaseSpeed();
            case AFRICAN:
                return getBaseSpeed() - getLoadFactor();
            case NORWEGIAN_BLUE:
                return isNailed ? 0 : getBaseSpeed();
            default:
                return 0;
        }
    }

    private double getLoadFactor() {
        return 3;
    }

    private double getBaseSpeed() {
        return 10;
    }
}

问题: 在添加新的类型时,我们必须要记得更新switch语句,此外随着不同bird的概念添加进来,bird类的凝聚力越来越糟。

适用范围:根据类型做单次切换是可行的,如果switch太多,在添加新类型时如果忘记更新现有隐藏类型中的所有switch,就会导致bug出现,8thlight博客关于这种情况有详细描述。

解决方案: 使用多态,添加新类型时大家都不会忘记添加相关行为。
注意:上例为了简洁只写了一个方法,但在有多个switch时更有用。
public abstract class Bird {

    public abstract double getSpeed();

    protected double getLoadFactor() {
        return 3;
    }

    protected double getBaseSpeed() {
        return 10;
    }
}

public class EuropeanBird extends Bird {
    public double getSpeed() {
        return getBaseSpeed();
    }
}

public class AfricanBird extends Bird {
    public double getSpeed() {
        return getBaseSpeed() - getLoadFactor();
    }
}

public class NorwegianBird extends Bird {
    private boolean isNailed;

    public double getSpeed() {
        return isNailed ? 0 : getBaseSpeed();
    }
}

模式3:NullObject/Optional
背景: 当外部请求理解代码库的主要用途时,回答“查一下null的情况”。
public void example() {
    sumOf(null);
}

private int sumOf(List<Integer> numbers) {
    if(numbers == null) {
        return 0;
    }

    return numbers.stream().mapToInt(i -> i).sum();
}

模式4:将内联语句(Inline statements)转为表达式
背景: 在计算布尔表达式时,包含if语句树。
public boolean horrible(boolean foo, boolean bar, boolean baz) {
    if (foo) {
        if (bar) {
            return true;
        }
    }

    if (baz) {
        return true;
    } else {
        return false;
    }
}

问题: 这种代码会导致开发者必须用大脑来模拟计算机对方法的处理。

适用范围:很少有不适用的情况,像这样的代码可以合成一行,或者拆成不同的部分。

解决方案: 将if语句树合成单个表达式。
public boolean horrible(boolean foo, boolean bar, boolean baz) {
    return foo && bar || baz;
}

模式5:给出应对策略
背景:在调用一些其他代码时,无法确保路径是成功的。
public class Repository {
    public String getRecord(int id) {
        return null; // cannot find the record
    }
}

public class Finder {
    public String displayRecord(Repository repository) {
        String record = repository.getRecord(123);
        if(record == null) {
            return "Not found";
        } else {
            return record;
        }
    }
}

问题: 这类if语句增加了处理同一个对象或者数据结构的时间,其中包含隐藏耦合——null的情况。其它对象可能会返回其他代表没有结果的magic value。

适用范围:最好将这类if语句放在一个地方,由于不会重复,我们就能将为空对象的magic value删除。

解决方案:针对被调用代码,给出应对策略。Ruby的Hash#fetch就是很好的案例,Java也用到了类似的方法。这种模式也可以用在删除例外情况时。
private class Repository {
    public String getRecord(int id, String defaultValue) {
        String result = Db.getRecord(id);

        if (result != null) {
            return result;
        }

        return defaultValue;
    }
}

public class Finder {
    public String displayRecord(Repository repository) {
        return repository.getRecord(123, "Not found");
    }
}

祝探索愉快
希望这些模式对你现在处理的问题有帮助。我在重构代码增进理解时,发现这些方法都很有用。要记得并非所有if语句都是魔鬼,不过现代编程语言还有很多功能值得我们探索并使用。
11
1
评论 共 6 条 请登录后发表评论
6 楼 zoozooll 2016-07-05 09:34
accountForTony 写道
模式3的解决方案是什么?

根据英文原文的解释,是不在这个方法里面做处理,而是直接调用的地方处理好。

public void example() {
    sumOf(new ArrayList<>());
}

private int sumOf(List<Integer> numbers) {
    return numbers.stream().mapToInt(i -> i).sum();
}
5 楼 18862611051 2016-07-04 15:20
4 楼 suchang1123 2016-06-29 13:36
方法里不加if,那就要在调用的时候加if,逻辑判断永远不会少。
3 楼 welcomezhang 2016-06-20 18:09
重要的是思维方式的转变
2 楼 welling319 2016-06-20 14:43
没有三元表达式
1 楼 accountForTony 2016-06-17 15:27
模式3的解决方案是什么?

发表评论

您还没有登录,请您登录后再发表评论

相关推荐

  • 如何在代码中减少if else语句的使用

    面向过程的开发中代码有大量的if else,在java中可以用一些设计模式替换掉这些逻辑,那么在js中是否也有类似的方法用来尽可能减少代码中的if/else嵌套呢?有人认为:if else多就多呗,只要可读性强,维护起来方便。...

  • java用什么替换if_在Java中替换掉繁杂的if语句

    通常我们会遇到一些需要做一系列...下面是带有两个数字类型参数,一个操作符参数以及基于操作的数值返回值的一个方法:public int calculate(int a, int b, String operator) {int result = Integer.MIN_VALUE;if (...

  • python的if语句中else可以省略吗_Python条件语句详解:if、else、switch都有了

    如果成立,则执行if语句内的程序;否则,跳过if语句,执行后面的内容。if语句的格式如下。if(表达式):语句1else:语句2if语句的执行过程如下:如果表达式的布尔值为真,则执行语句1;否则,执行语句2。其中的else子句...

  • c#语言中if语句多条件,c# – IF语句多个条件相同的语句

    嘿,所有,寻求减少我的c#if语句的代码,因为有几个重复的因素,并且想知道是否可以修剪修补程序.我目前有2个if语句需要执行相同的语句,但是当没有选中复选框时,唯一的变量是if语句的额外条件.我只是想知道是否有一种...

  • (C语言)if语句详解,理清逻辑,if语句随便用

    同时通过养成写完if()条件后加大括号{}的习惯,可以使我们的代码更有条理更清楚,可以让我在以后写复杂if语句时。当面对悬空else时,我们需要记住:当存在多个if和else时,else 总是跟最接近 的 if 匹配。如果我们...

  • c++if语句(else if)if嵌套语句

    if语句是一种基本的条件控制语句,它的语法基本跟自然语言中的英语一样(if something happens, do something)。if语句后面还可以选择的加上 else 语句(if something happen,do something ,else ,do other ...

  • python3if语句_3种替代if语句的方法,使您的python代码更具可读性

    python3if语句Are you taking advantage of these features of Python that can help make your code make more sense?您是否正在利用Python的这些功能来帮助使代码更有意义? Code readability is not just about ...

  • java if 用法详解_Java编程中的条件判断之if语句的用法详解

    if语句使用布尔表达式或布尔值作为分支条件来进行分支控制,其中if语句有如下三种形式:第一种形式:if ( logic expression ){statements…}第二种形式:if (logic expression){statements…}else{statements…}第三...

  • python的else if语句_Python if else语句

    if 语句是用来检查一个条件:如果条件为真(true),我们运行一个语句块(你为if块),否则(else),我们执行另一个语句块(称为else块)。else子语句是可选的。例如(保存为 if.py):number = 23guess = int(input(‘请输入...

  • python中多重if语句用法_python-循环语句的简单条件语句、多重条件语句和嵌套条件语句编写...

    1、 简单if-else语句例1:要求用户输入两个数,计算两数的和。如果这两个数的和大于100,则输出“两数和大于100”,否则输出“两数和小于100”。程序要求用户从键盘输入两个整数,求这两个整数的和。如果和大于100,...

  • c语言入门----详解分支语句(if语句)

    小白快来两万字带你充分了解if语句。

  • matlab 连续两个if,求大神解答一个matlab中的for循环嵌套if选择语句

    话题:求大神解答一个matlab中的for循环嵌套if选择语句回答:1、最后缺少一个end;2、这句输出没什么意义:disp A(i)=A(j);3、这句完全多余:else continue给你写了一段码供参考:% 生成测试数据A = [randperm(19) ...

  • c++ 条件控制语句 if语句 switch语句

    if语句; 多行 if语句; else if的应用 ; 悬垂 else ; switch语句的贯穿的合理应用; 遗漏default;

  • 为什么建议少用 if 语句

    if-else 作为三种最基本的程序结构之一,是我们从最开始学习编程时就接触的基本语句。但是到后面的阶段就不断听人说少用if-else。 如果询问原因的话,你得到的结果大概率是if-else导致程序运行效率下降。这次来扯扯...

  • Java的if语句

    if 语句是使用最多的条件分支结构。 if 语句的最简语法格式如下,表示“如果满足某种条件,就进行某种处理”。 if (条件表达式) { 语句块; } 其中“条件表达式”和“语句块”是比较重要的两个地方。 条件...

  • Java中的If-Then和If-Then-Else条件语句

    if-then and if-then-elseconditional statements let a 条件语句使Java program make Java程序可以对下一步的操作做出simple decisions about what to do next. They work in the same logical way as we do when ...

  • 关于FPGA中锁存器的生成:if语句和case语句的完整性影响

    刚入门FPGA,学习Verilog语言编写代码,总听别人说要规范写代码的习惯,其中一个就是if…else以及case语句,需要些完整,比如if后必须有else,case语句最好有default,不然会产生意想不到的锁存器,一直不明白锁存器...

  • python判断语句_详解Python判断语句的使用方法

    ● 掌握判断语句的使用规则● 判断语句流程图的画法前面我们学习了Python的数据类型和相关知识,现在可以开始编写Python程序了,首先从条件判断开始。1、 简单if-else语句例1:要求用户输入两个数,计算两数的和。...

  • Java利用if语句来实现中国个人所得税的计算方法

    Java利用if语句来实现中国个人所得税的计算方法 注意:现在国家税收早已改革不要在意这些细节,注意看题! 应税所得为税前收入扣除 3500 元(起征点),然后超出部分,按照以下税率收税: 规则:应缴税款 = (税前...

Global site tag (gtag.js) - Google Analytics