13 设计原则 :KISS 原则和YAGNI 原则

134 阅读5分钟

女朋友嫌我写代码烂,被分手,学习设计原则的第二天,写到这里就泪流面面了,不知道还有几只单身狗跟我一样,代码烂且没有女朋友。

1. 什么是KISS原则?

1.1 定义

KISS原则,挺好记,“Keep It Simple, Stupid”该原则强调在设计和实现软件系统时应保持简单,避免过度复杂化。简而言之,KISS原则鼓励以简单、直接的方式解决问题,避免不必要的复杂性。

1.2 那怎么样才算是简单呢?

有没有黄金原则,来指导我写实际的代码?代码多简单算是遵循了KISS原则?说谁都会说,举个例子看看?

一个合法的 IP 地址由四个数字组成,并且通过“.”来进行分割。每组数字的取值范围是0~255。第一组数字比较特殊,不允许为 0。对比这三段代码,你觉得哪一段代码最符合KISS 原则呢?如果让你来实现这个功能,你会选择用哪种实现方法呢?你可以先自己思考一下,然后再看我下面的讲解。

// 第一种实现方式: 使用正则表达式
public boolean isValidIpAddressV1(String ipAddress) {
    if (StringUtils.isBlank(ipAddress)) return false;
    String regex = "^(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[1-9])\\."
    + "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."
    + "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."
    + "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)$";
    return ipAddress.matches(regex);
}
// 第二种实现方式: 使用现成的工具类
public boolean isValidIpAddressV2(String ipAddress) {
    if (StringUtils.isBlank(ipAddress)) return false;
    String[] ipUnits = StringUtils.split(ipAddress, '.');
    if (ipUnits.length != 4) {
        return false;
    }
    for (int i = 0; i < 4; ++i) {
    int ipUnitIntValue;
        try {
        ipUnitIntValue = Integer.parseInt(ipUnits[i]);
        } catch (NumberFormatException e) {
            return false;
        }
        if (ipUnitIntValue < 0 || ipUnitIntValue > 255) {
            return false;
        }
        if (i == 0 && ipUnitIntValue == 0) {
            return false;
        }
    }
    return true;
}
// 第三种实现方式: 不使用任何工具类
public boolean isValidIpAddressV3(String ipAddress) {
    char[] ipChars = ipAddress.toCharArray();
    int length = ipChars.length;
    int ipUnitIntValue = -1;
    boolean isFirstUnit = true;
    int unitsCount = 0;
    for (int i = 0; i < length; ++i) {
    char c = ipChars[i];
        if (c == '.') {
            if (ipUnitIntValue < 0 || ipUnitIntValue > 255) return false;
            if (isFirstUnit && ipUnitIntValue == 0) return false;
            if (isFirstUnit) isFirstUnit = false;
            ipUnitIntValue = -1;
            unitsCount++;
            continue;
        }
        if (c < '0' || c > '9') {
            return false;
        }
        if (ipUnitIntValue == -1) ipUnitIntValue = 0;
            ipUnitIntValue = ipUnitIntValue * 10 + (c - '0');
    }
    if (ipUnitIntValue < 0 || ipUnitIntValue > 255) return false;
    if (unitsCount != 3) return false;
    return true;
}

第一种实现方式利用的是正则表达式,只用三行代码就把这个问题搞定了。它的代码行数最少,那是不是就最符合 KISS 原则呢?答案是否定的。虽然代码行数最少,看似最简单,实际上却很复杂。这正是因为它使用了正则表达式。 一方面,正则表达式本身是比较复杂的,写出完全没有 bug 的正则表达本身就比较有挑战;另一方面,并不是每个程序员都精通正则表达式。对于不怎么懂正则表达式的同事来说,看懂并且维护这段正则表达式是比较困难的。这种实现方式会导致代码的可读性和可维护性变差,所以,从 KISS 原则的设计初衷上来讲,这种实现方式并不符合 KISS 原则。

不过,尽管第三种实现方式性能更高些,但我还是更倾向于选择第二种实现方法。那是因为第三种实现方式实际上是一种过度优化。除非 isValidIpAddress() 函数是影响系统性能的瓶颈代码,否则,这样优化的投入产出比并不高,增加了代码实现的难度、牺牲了代码的可读性,性能上的提升却并不明显。

摘抄了一部分,感觉味同嚼蜡,有没有使用的知识点学习下呢?

2. 怎么使用KISS原则?

  • 不要使用同事可能不懂的技术来实现代码。比如前面例子中的正则表达式,还有一些编程语言中过于高级的语法等。(实际中我们偏偏要这么做,哈哈哈哈哈,最近阿里和滴滴因为裁人,导致服务器挂掉了,业务损失严重。)
  • 不要重复造轮子,要善于使用已经有的工具类库。经验证明,自己去实现这些类库,出bug 的概率会更高,维护的成本也比较高。
  • 不要过度优化。不要过度使用一些奇技淫巧(比如,位运算代替算术运算、复杂的条件语句代替 if-else、使用一些过于底层的函数等)来优化代码,牺牲代码的可读性。

1. 什么是YAGNI原则?

YAGNI 原则的英文全称是:You Ain’t Gonna Need It。它的意思是:不要去设计当前用不到的功能;不要去编写当前用不到的代码。实际上,这条原则的核心思想就是:不要做过度设计

比如说我们做一个上报数据的需求,目前使用的是实时通讯模块,同样抽象了非实时上报数据的接口,那我们要不要去代码提前实现呢?

假如实现了,这就是过度设计。

  1. 当前业务我们还用不到
  2. 开发当前功能需要额外排期
  3. 后续真正用到了,产品需求不一定是如此,还要额外修改
  4. 引入了代码,就引入了隐患

这一节知识点思想大于实际应用,没有引经据典的例子,更多的是考虑代码理解,需要在以后的实际开发中多多体会。