如何理解 KISS 原则?

KISS 原则的英文描述有好几个版本:

Keep It Simple and Stupid
Keep It Short and Simple
Keep It Simple and Straightforward

它们表达的意思其实都差不多,其实就是:尽量保持简单

我们知道,代码的可读性和可维护性是衡量代码质量非常重要的两个标准。而 KISS 原则就是保持代码可读性和可维护性的重要手段。代码足够简单,也就意味着很容易读懂,bug比较难隐藏。即便出现bug,修复起来也比较简单。

代码行数越少就越“简单”吗?

我们先来看一个例子。下面三段代码可以实现同样一个功能:检查输入的字符串 ipAddress 是否是合法的IP地址。

// 第一种实现方式:使用正则表达式
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 原则。

第二种实现方式和第三种实现方式,更倾向于选择第二种实现方式。为什么?第三种方式通过逐一处理IP地址中的字符来判断是否合法,相比第二种使用工具类的方式更加有难度,更容易写出bug。第二种实现方式代码逻辑更清晰、更好理解,更加符合 KISS 原则。

不过,你可能会说,第三种实现方式虽然实现起来稍微有点复杂,但性能要比第二种方式高一些。

其实,第三种方式是属于过度优化除非 isValidIpAddress() 是影响系统新性能的瓶颈代码,否则,这样优化的投入产出比并不高,增加了代码实现的难度、牺牲了代码的可读性,性能上的提升却并不明显

代码逻辑复杂就违背 KISS 原则吗?

刚刚我们提到,并不是代码行数越少就越 “简单”,还要考虑逻辑复杂度、实现难度、代码的可读性等。那如果一段代码的逻辑复杂、实现难度大、可读性也不太好,是不是就一定违背 KISS 原则?回答这个问题之前,我们先来看下这段代码:

// KMP algorithm: a, b分别是主串和模式串;n, m分别是主串和模式串的长度
public static int kmp(char[] a, int n, char[] b, int m) {int[] next = getNexts(b, m);int j = 0;for (int i = 0; i < n; ++i) {while (j > 0 && a[i] != b[j]) {j = next[j - 1] + 1;}if (a[i] == b[j]) {++j;}if (j == m) {return i - m + 1;}}return -1;
}// b表示模式串,m表示模式串的长度
private static int[] getNexts(char[] b, int m) {int[] next = new int[m];next[0] = -1;int k = -1;for (int i = 1; i < m; ++i) {while (k != -1 && b[k + 1] != b[i]) {k = next[k];}if (b[k + 1] == b[i]) {++k;}next[i] = k;}return next;
}

上面的代码是 KMP 字符串匹配算法的代码实现。这段代码完全符合我们刚提到的逻辑复杂、实现难度大、可读性差的特点,但它并不违背KISS原则。如果选择使用 KMP 算法是因为系统瓶颈,本身就复杂的问题,用复杂的方法解决,并不违背 KISS 原则

不过,平时的项目开发如果问题不影响性能瓶颈,能用编程语言提供的现成的函数就能足够的情况,还是非得用 KMP 算法等复杂方式实现,那就真的违背 KISS 原则了。

如何写出满足 KISS 原则的代码?

实际上,我们前面已经讲到了一些方法,这里稍微总结一下:

  • 不要使用同时可能不懂的技术来实现代码。比如前面例子中的正则表达式,还有一些编程语言中过于高级的语法等

  • 不要重复造轮子,要善于使用已有的工具类库。经验证明,自己去实现这些类库,出bug的概率会更高,维护的成本也比较高

  • 不要过度优化。不要过度使用一些奇技淫巧(比如,位运算替代算数运算、复杂的条件语句代替if-else、使用一些过于底层的函数等)来优化代码,牺牲代码的可读性

实际上,判断代码是否足够简单是一个挺主观的判断,同样的代码有人觉得简单,有人觉得不够简单。评判代码是否简单,还有一个很有效的间接方法,那就是code review。如果在code review时,同事对你的代码有很多疑问,那就说明你的代码可能不够“简单”需要优化。

还有,一定不要过度设计,不要觉得简单的东西就没有技术含量。实际上,越是能用简单的方法解决复杂的问题,越能体现一个人的能力

YAGNI 跟 KISS 说的是一回事吗?

YAGNI(You Ain’t Gonna Need It)原则,直译就是:你不会需要它。它的意思是:不要去设计当前用不到的功能;不要去编写当前用不到的代码这条原则的核心思想就是:不要过度设计

当然,这并不是说我们就不需要考虑代码的扩展性,我们还是要预留好扩展点。再比如,我们不要再项目中提前引入不需要依赖的开发包,这样的做法也是违背YAGNI原则。

从刚刚的分析我们可以看出,YAGNI 原则跟 KISS 原则并非一回事。KISS 原则讲的是 “如何做” 的问题(尽量保持简单),而 YAGNI 原则说的是 “要不要做” 的问题(当前不需要的就不要做)

总结

KISS 原则是保持代码可读性和可维护性的重要手段。关于 KISS 原则可以有以下总结:

  • KISS 原则中的 “简单” 并不是以代码行数来考量的。代码行数越少并不代表代码越简单,我们还要考虑逻辑复杂度、实现难度、代码的可读性等

  • 本身就复杂的问题,用复杂的方式解决,并不违背KISS原则

  • 同样的代码,在某个业务场景下满足 KISS 原则, 换一个应用场景可能就不满足了

对于如何写出 KISS 原则的代码,有以下几条指导原则:

  • 不要使用同事可能不懂得技术来实现代码

  • 不要重复造轮子,要善于使用已经有得工具类库

  • 不要过度优化

KISS原则和YAGNI原则相关推荐

  1. 设计原则之KISS原则和YAGNI原则

    设计原则之KISS原则和YAGNI原则 KISS原则 KISS(Keep It simple and Stupid)原则总的来说就是简单,你的代码要写的简单易懂.增加代码的可读性. 并不一定是代码量的 ...

  2. 设计原则—KISS原则和YAGNI原则

    怎么理解kiss原则中的"简单"两字? 什么样的代码才算简单? 怎样的代码才算复杂? 如何才能写出简单代码? YAGNI原则和KISS原则说的是一回事吗? KISS原则的英文版本描 ...

  3. 【设计原则】KISS原则与YAGNI原则

    KISS,Keep It Short and Simple. KISS 原则是保持代码可读和可维护的重要手段.KISS 原则中的"简单"并不是以代码行数来考量的.代码行数越少并不代 ...

  4. 一文搞懂Kiss、Yagni原则

    Kiss原则 定义 尽量保持简单 目的 Kiss原则是保持代码可读性.可维护性的有效手段,代码简单那么就容易理解,bug就越难隐藏,即便出现bug修复起来也比较简单 代码行数越少就越"简单& ...

  5. yagni原则_YAGNI喜欢干吻

    yagni原则 有几个首字母缩写词肯定会帮助您审查代码和设计,对其进行改进或确认所讨论的决策,因为它们实际上是设计范例和经过验证的良好实践. 很难将所有这些都组合成一个易于记忆的句子,即使出现的列表并 ...

  6. 设计原则之 SOLID 原则

    以下是在极客时间<设计模式之美> 中的写学习笔记与心得的总结 在最初开始学设计模式的时候,总觉的要学的是那23种经典的设计模式.通过一段的学习,才突然领悟,设计原则才是王道,才是真正的内功 ...

  7. 【设计模式】软件设计七大原则 ( 合成复用原则 | 代码示例 )

    文章目录 一.合成复用原则简介 二.合成复用 与 继承复用 优缺点 三.合成复用原则代码示例 1.继承复用代码示例 2.合成复用代码示例 一.合成复用原则简介 合成复用原则 又称为 组合复用原则 , ...

  8. 【设计模式】软件设计七大原则 ( 接口隔离原则 | 代码示例 )

    文章目录 一.接口隔离原则简介 二.接口隔离原则代码示例 ( 反面示例 ) 1.接口定义 ( 接口臃肿 ) 2.实现类 1 3.实现类 2 三.接口隔离原则代码示例 ( 推荐用法 ) 1.接口 1 2 ...

  9. 软件设计原则——里氏代换原则

    里氏代换原则 里氏代换原则是面向对象设计的基本原则之一. 里氏代换原则:任何基类可以出现的地方,子类一定可以出现. 通俗理解:子类可以扩展父类的功能,但不能改变父类原有的功能. 换句话说,子类继承父类 ...

最新文章

  1. Boost 编译链接
  2. 偏差/方差、经验风险最小化、联合界、一致收敛
  3. 创建azure服务器
  4. VS2008中使用JSONCPP方法小结
  5. PID:我应该何时计算积分项?
  6. 关于码云的一些基本知识_一些关于 CPU 的基本知识
  7. Storm概念学习系列之Task任务
  8. pytorch中调整学习率: torch.optim.lr_scheduler
  9. 【安全组网】思科IOS设备基础应用
  10. EhCache的使用
  11. 乞丐的一句话,感动中国13亿人。
  12. (struct)结构体变量作为函数参数调用的方法小结
  13. Windows连接阿里云服务器图形界面
  14. 设计模式(九)——代理模式(Proxy)
  15. U3D场景制作规范(转)
  16. 调用百度AI开放平台实现图片文字识别
  17. 如何打造数字原生企业?易捷行云EasyStack有话要说
  18. 【图像超分辨率重建】——GRL论文精读笔记
  19. WHQL认证如何给驱动程序做数字签名
  20. 五大最受欢迎的BUG管理系统 .

热门文章

  1. oracle 无metalink账号补丁下载方法
  2. 在微信小程序中使用iconfont
  3. 德国大陆4D毫米波雷达ARS548开发
  4. SECS/GSM 通讯中转工具
  5. 解决VS “无法导入以下密钥文件” 错误
  6. doris 分片与副本
  7. oracle 触发器定时任务,ORACLE触发器,定时器。
  8. SecureCRT常用命令分享 SecureCRT命令大全
  9. 罗斯蒙特3051酸碱盐浓度变送器产品简介及原理
  10. ThinkPad黑将S笔记本进bios设置u盘启动教程