为什么《程序员修炼之道》评分高达 9.1?

京东云开发者
• 阅读 48

作者:京东保险 王奕龙

开始接触到《程序员修炼之道:通向务实的最高境界》这本书是在豆瓣图书的高分榜单上,它的评分高达 9.1,其中有条蛮有意思的书评非常吸引我:“这本书我读过 5 遍信不信,每个字都磨出了感情... 爱看技术书的程序员,看看可以往上走走;不爱看技术书的程序员,看看可以轻松刷出阅读成就感”。所以,本着刷阅读成就感并希望磨炼技术的态度便开始了本书的阅读,抽业余时间读完,其中有部分收获能和大家分享,当然更希望大家去看原书。

1. DRY 原则

DRY 是书中强调和多次出现的原则,它的中文释义是“干的;干燥的”。在文中强调的是:在系统中,对于每一处的知识都要保持单一和明确。初看上去这个原则是值得坚持和肯定的,但其中提到了关于注释不要重复代码实现的想法,我对此并不是很同意,我觉得在接口层加入详细的注释还是有必要的。

以较为复杂的查询接口为例,如果不添加详细的注释的话,那么调用者需要深入到接口实现中去找、去看必要了解的知识,如果这个查询接口比较复杂,那么便需要花费比较多的时间。反之,如果有详细的注释,那么便节省了翻看实现的时间,比如说如下查询推荐延保的接口,简略注释只是对方法名的翻译,至于返回值是什么,请求参数需要哪些赋值需要去具体实现中寻找;详细注释则注明了这些内容。

/**
 * 简略注释:查询推荐的延保
 * 详细注释:结果中推荐延保数量至多为 2;入参中分页查询信息 ...
**/
Result queryRecommand(Request req);

但书中为什么反对注释重复方法的实现呢?因为它担心修改实现时,忘记维护注释使得注释过时。虽然有理,但是我觉得如果 将注释看成代码的一部分,并约束修改代码时,同时修改注释,是能够避免这个问题的,这样不仅仅是提高可读性,还能提高接口的抽象程度。

不过,DRY 原则也有值得坚持的地方,以如下线段类定义为例:

class Line {
    Point start;
    Point end;
    double length;
}

第一眼看上去貌似没什么问题,线段有起点、终点和长度。但是实际上出现了重复:长度是由起点和终点定义而来的,改变其中之一那么便将引起长度的变化,最好是把长度的定义变成方法,如下:

class Line {
    Point start;
    Point end;

    public double length() {
      return end.distanceTo(start);
    }
}

这样消除了重复。但是如果该计算非常耗费性能,这样定义可能并不合适,还是需要为长度计算的结果,冗余出字段来保存,如下:

class Line {
    private Point start;
    private Point end;
    private double length;

    public void setStart(Point p) {
      this.start = p;
      calculateLength();
    }

    private void calculateLength() {
      this.length = end.distanceTo(start);
    }

    public double length() {
      return length;
    }
}

所以,DRY 原则还是需要被辩证地看待。当然,程序中最明显的代码重复还是非常值得去处理的,将它们抽象提出来,能够 让复用变得更容易

2. 继承税

“少用继承,多用组合” 是之前在学习设计模式时接触到的原则,但是当时我对此并没有什么感触,甚至觉得继承蛮好用的,比如在应用模板方法模式时,使用抽象类来定义方法模板。不过在书中又提到了这个原则,它称之为“继承税”,并做出了一段蛮有意思的描述:

你想要一根香蕉,但得到的却是一只拿着香蕉的大猩猩,甚至还有整个森林

其表达的意思也不难理解:强调继承带来的父类与子类之间的 耦合太深 了,父类中通用字段、方法的变更对子类来说可能带来意想不到的后果

为什么《程序员修炼之道》评分高达 9.1?

以如上继承关系为例,如果最高父类中某些内容发生变更,子类中对其使用的话,那么可能会引起子类行为的改变,而这种改变没有导致编译时异常,可能是没办法发现的,这样使得代码的可维护性大大降低,而且维护在每个类中的知识会在继承关系之间波动,暴露了太多的知识出来,做不到抽象和信息隐藏。

那么不用继承该怎么办呢?

  1. 使用 接口实现来代替类的继承,保证多态性又不会造成信息的紧耦合
  2. 使用 组合代替继承:比如想要香蕉,那么直接将包含香蕉的类注入进来,不再通过继承去获取了

从这也能理解为什么 C++ 语言中的多继承被诟病。此外,我觉得继承也并不能被一票否决,在 Java 源码中常用容器的实现里,都是有抽象层的(AbstractList, AbstractMap 等等),通过继承它们,实现了大量代码复用,为各种不同容器的具体实现提供了很多方便之处。但是我觉得继承能被这么应用需要具备前提条件:一是 抽象出来的父类不会或很少再变动;二是 开发者变动的前提是对此有清晰的了解

如果不是这样,在业务代码中引入继承树,那带来的复杂性就太高了。

3. 重构

先前我对重构的观点是:如果能用,尽量避免重构,当代码实在难以满足需求时,再推翻它重新来。但是书中提出的观点则不同:代码需要演化,它不是静态的东西。 当遇到绊脚石或是注意到有两件事的确需要合并,又或是被其他什么事情触动而心生悔意时,那么请不要犹豫,去改掉它。并且它主张的重构是一项日复一日、小步快走的工作,并不是“大厦的倒塌重建”,这样低风险小步骤进行改造有助于使代码更易于维护和更改。

不过理想总是好的,在现实中重构总会面临一些问题:

  1. 时间压力:这个需求预计 3 天能开发完,但是为了优化代码设计和逻辑,需要增加 2 天时间。增加出的额外时间,可能并不会被接受
  2. 改动带来的风险:如何才能保证重构的影响全部在可控的范围内非常值得思考,如果重构会引发 Bug,那么开发者会宁愿重构并没有开始

事实上,时间压力并不太能站得住脚,因为随着功能增加,复杂度会不断累积,那么未来便需要投入更多的时间来修复,并且将引发更大规模的改动,带来 Bug 的风险也会增加;而对于改动带来的风险,我觉得本书强调“小步、多次”重构也是想将此风险降低,它更像是一项慢慢地、有意地、仔细地进行的活动,除此之外使重构安全的方法是在重构之后有良好的测试,如果我们有 完善的业务场景的单元测试用例,在重构完能及时发现问题所在,也不至于对重构这件事情畏手畏脚了。

此外,不对代码进行重构往往会触发开发者心中的“破窗效应”,在已经很难维护的代码上继续叠加功能,而不是对其进行改善,使得代码更加难以维护,还会拿“这段代码已经很烂了”作为“合理的借口”。所以,重构该成为日常开发中,需要注意和进行的活动。

破窗效应:一种社会心理学理论,它表明环境的恶化会导致人们行为的恶化。该理论认为,如果一个社区的某个小问题没有及时得到修复,那么这个小问题的存在就会给人一种信号,即这个地方被忽视或者管理不善。这种信号会诱使人们模仿这种不良行为,从而导致更多的窗户被破坏,最终可能导致整个社区的秩序崩溃。

4. 命名

每次提到命名或者在为接口命名时,我都会有一种非常强烈的让它自解释的想法,但是我最近这种想法的欲望逐渐降低,原因有二:

  1. 阅读习惯:对国人来说,可能大多数人没有先去读英文的习惯,更倾向于读中文相关的内容,比如注释
  2. 英语水平参差:可能有时候想要自解释的初心是好的,但是如果使接口名变成了长难句,可读性将降低

即使是这样,也并不能降低命名的标准,应该有一个适度的折中:不引入长难句,将其中难以表达的内容考虑使用注释来补充。此外,我觉得命名保持一致性也非常重要,比如在项目中对于补购已经命名为 AddBuy,那么便不要再引入 SupplementaryPurchaseReplenishment 等命名,团队内成员将知识统一才是最好的,并不在于它在英文语境下是否表达准确。但是对于这一项工作,我还没有发现团队花费心血来做这件事,如果不去翻看原有代码的话,冒然的命名可能不符合系统内现有规范,所以我觉得可以创建相关的文档或者在 README 中将这些命名规范记录下来,这不光降低了命名难度,而且使得团队内成员能够统一,也方便交流。

除了在方法自解释上下功夫外,方法的表达也值得注意,比如定义一个打折的方法:

void deductPercent(double amount);

deductPercent 扣除百分比指的是 要做的事情,但是扣除什么的百分比是不明确的,其次,入参 amount 也容易让人疑惑,是绝对值呢?还是百分数?应该有几位小数?所以,换一种方式会更好一些:

void applyDiscount(Percentage discount);

applyDiscount 方法名表达了折扣的意图,并且将 double 类型换成了对象类型,在对象中进行准确的定义,也是一种方法。当然,如果仍然采用 double 类型的入参也没有问题,在注释中注明容易让人迷惑的部分也是不错的方案。

5. 终

我觉得这本书更多的是在传达一种 务实的工程师精神和责任,不只是要编写好的代码,还要为你编写的代码负责,积极地为编码添加上 @author 的标志,当开发者看到你的名字时,他们能联想这段逻辑是可靠的、可读性好的和易于维护的,也是专业性的体现。

除了本文中提到的内容之外,其中还有关于基础工具的使用、编码的习惯、需求管理和人生哲学等内容,我觉得用它的结尾来作为本文的结尾也再合适不过:

你要为自己的人生做主,精心营造,与人分享,并为之喝彩!

点赞
收藏
评论区
推荐文章
Karen110 Karen110
3年前
醒醒!Python已经支持中文变量名啦!
作者:豌豆花下猫来源:Python猫最近,我在翻阅两本比较新的Python书籍时,发现它们都犯了一个严重的低级错误!这两本书分别是《Python编程:从入门到实践》和《父与子的编程之旅》,它们都是畅销书,都在2020年10月出了新版本,都使用Python3.7版本的语法。然而,在关于变量的命名规则部分,它们犯下了一样的错误,即还在使用Py
python知道 python知道
3年前
流畅的pythonPDF高清版
提取码:1028内容简介······【技术大咖推荐】“很荣幸担任这本优秀图书的技术审校。这本书能帮助很多中级Python程序员掌握这门语言,我也从中学到了相当多的知识!”——AlexMartelli,Python软件基金会成员“对于想要扩充知识的中级和高级Python程序员来说,这本书是充满了实用编程技巧的宝藏。”——DanielGreenf
Aidan075 Aidan075
3年前
用python爬取《龙岭迷窟》评论,看看比同系列鬼吹灯作品以及《盗墓笔记》好在哪里?
↑关注星标  有趣的不像个技术号每晚九点,我们准时相约  大家好,我是朱小五最近不知道大家发现没有,新出了几部国产好剧,其中小五比较喜欢的就是鬼吹灯系列的《龙岭迷窟》。自从开播以来,获得好评无数,豆瓣评分开播8.4分,目前有所回落,维持在8.2分,无论是原著粉还是路人观众,都对这部新网剧赞誉有加。在《鬼吹灯》系列的众多影视化作品中名
Stella981 Stella981
3年前
Sass用法指南
学过CSS的人都知道,它不是一种编程语言。你可以用它开发网页样式,但是没法用它编程。也就是说,CSS基本上是设计师的工具,不是程序员的工具。在程序员眼里,CSS是一件很麻烦的东西。它没有变量,也没有条件语句,只是一行行单纯的描述,写起来相当费事。很自然地,有人就开始为CSS加入编程元素,这被叫做"CSS预处理器"(csspreproces
Wesley13 Wesley13
3年前
JAVA程序员应该读哪些书(豆瓣8.0+)
!(https://oscimg.oschina.net/oscnet/d6fe68b330464e67b00e702363070857.gif)架构相关从零开始学架构(李运华)豆瓣评分8.3,极客时间有专栏,跟着做,你也可以成为架构师。企业IT架构转型之道:阿里巴巴中
Wesley13 Wesley13
3年前
Java程序员的推荐阅读书籍
作为Java程序员来说,最痛苦的事情莫过于可以选择的范围太广,可以读的书太多,往往容易无所适从。我想就我自己读过的技术书籍中挑选出来一些,按照学习的先后顺序,推荐给大家,特别是那些想不断提高自己技术水平的Java程序员们。  一、Java编程入门类  对于没有Java编程经验的程序员要入门,随便读什么入门书籍都一样,这个阶段需要你快速的掌握Java基
Easter79 Easter79
3年前
StackOverflow程序员推荐:每个程序员都应读的30本书
“如果能时光倒流,回到过去,作为一个开发人员,你可以告诉自己在职业生涯初期应该读一本,你会选择哪本书呢?我希望这个书单列表内容丰富,可以涵盖很多东西。”很多程序员响应,他们在推荐时也写下自己的评语。以前就有国内网友介绍这个程序员书单,不过都是推荐数Top10的书。其实除了前10本之外,推荐数前30左右的书籍都算经典,伯乐在线整理编译这个问答贴,同时摘
京东云开发者 京东云开发者
2个月前
「软件设计哲学」于延保代码改造中的实践
作者:京东保险王奕龙本文主要给大家分享软件设计中的两个理念,为什么我称软件设计是“理念”而不是“方法”或“原则”呢?这个想法主要受《Aphilosophyofsoftwaredesign》的影响,它将软件设计称为“哲学”,而哲学本身没有严格的定论,同样地,
京东云开发者 京东云开发者
1个月前
分布式服务高可用实现:复制
作者:京东保险王奕龙1.为什么需要复制我们可以考虑如下问题:1.当数据量、读取或写入负载已经超过了当前服务器的处理能力,如何实现负载均衡?2.希望在单台服务器出现故障时仍能继续工作,这该如何实现?3.当服务的用户遍布全球,并希望他们访问服务时不会有较大的延
京东云开发者 京东云开发者
2小时前
由 Mybatis 源码畅谈软件设计(一):序
作者:京东保险王奕龙从接触软件开发以来,一直对写出优雅的代码抱有执念,工作半年时,偶然接触到《代码整洁之道》,爱不释手,一口气读完,并在很长的时间内践行其中的观点,但是在这践行期间缺少思考和复盘,更多的是一味地信奉和遵守其中的原则,写了不少当时自认为不错而