《万字详文阐释程序员修炼之道》书摘

一直以来,设计(Design)和架构(Architecture)这两个概念让大多数人十分迷惑——什么是设计? 什么是架构?二者究竟有什么区别?二者没有区别。一丁点区别都没有!

写文档是个好习惯。但是写一个别人需要咨询老开发者才能找到的文档,是个坏习惯。这个坏习惯甚至会给工程师们带来伤害。

代码首先要自解释,当解释不了的时候,需要就近、合理地写注释。

解释信息必须离被解释的东西,越近越好。代码能做到自解释,是最棒的。

ETC 是一种价值观念,不是一条原则。

ETC 价值观(easy to change)

持有 ETC 价值观看待代码细节、技术方案,我们将能更好地编写出适合敏捷项目的代码。

DRY 原则(don not repeat yourself)

'正交性'是几何学中的术语。我们的代码应该消除不相关事物之间的影响。

全局变量是不正交的!没有充分的理由,禁止使用全局变量。

单例就是全局变量

可逆性,就是你做出的判断,最好都是可以被逆转的。

依赖倒置原则,全称是 Dependence Inversion Principle,简称 DIP。

TDD(Test-Driven Development)是自底向上地编码过程,其实会耗费大量的精力,并且对于一个良好的层级架构没有帮助。

TDD 方法论实现的接口、函数,自我解释能力一般来说比较强,因为它就是一个实现契约的过程。

Erlang 和 Elixir 语言信奉这种哲学。乔-阿姆斯特朗,Erlang 的发明者,《Erlang 程序设计》的作者, 有一句反复被引用的话: "防御式编程是在浪费时间,让它崩溃"。

尽早崩溃不是说不容错,而是程序应该被设计成允许出故障,有适当的故障监管程序和代码,及时告警,告知工程师,哪里出问题了,而不是尝试掩盖问题,不让程序员知道。

继承就是耦合。不仅子类耦合到父类,以及父类的父类等,而且使用子类的代码也耦合到所有祖先类。

结构化编程是对程序控制权的直接转移的限制。

面向对象是对程序控制权的间接转移的限制。

函数式编程是对程序中赋值操作的限制。

SOLID 原则(单一功能、开闭原则、里氏替换、接口隔离、依赖反转,后面会讲到)是 OOP 编程的最经典的原则。

Golang 里大家耳熟能详的谚语: "不要通过共享内存来通信,而应该通过通信来共享内存"。

不要在正常 case 下打印日志。

不要在单元测试里使用 fmt 标准输出,至少不要提交到 master。

不打不必要的日志。当错误出现的时候,会非常明显,我们能第一时间反应过来并处理。

让调试的日志停留在调试阶段,或者使用较低的日志级别,你的调试信息,对其他人根本没有价值。

即使低级别日志,也不能泛滥。不然,日志打开与否都没有差别,日志变得毫无价值。

SOLID 原则,是由以下几个原则的集合体: SRP: 单一职责原则OCP: 开闭原则LSP: 里氏替换原则ISP: 接口隔离原则DIP: 依赖反转原则

模块原则: 使用简洁的接口拼合简单的部件

清晰原则: 清晰胜于技巧

组合原则: 设计时考虑拼接组合

分离原则: 策略同机制分离,接口同引擎分离

简洁原则: 设计要简洁,复杂度能低则低

吝啬原则: 除非确无它法,不要编写庞大的程序

透明性原则: 设计要可见,以便审查和调试

健壮原则: 健壮源于透明与简洁

表示原则: 把知识叠入数据以求逻辑质朴而健壮

通俗原则: 接口设计避免标新立异

缄默原则: 如果一个程序没什么好说,就保持沉默

补救原则: 出现异常时,马上退出并给出足量错误信息

经济原则: 宁花机器一分,不花程序员一秒

生成原则: 避免手工 hack,尽量编写程序去生成程序

优化原则: 雕琢前先得有原型,跑之前先学会走

多样原则: 绝不相信所谓"不二法门"的断言

扩展原则: 设计着眼未来,未来总比预想快

如果没有充分的理由,始终使用项目规范的范式对每一类问题做出解决方案。

如果业务发展发现老的解决方案不再优秀,做整体重构。

项目级主干开发,对重构很友好,让重构变得可行。(客户端很容易实现主干开发)。

务实地讲,重构已经不可能了。那么,你们可以谨慎地提出新的一整套范式。重建它。

禁止 hardcode,特殊逻辑。如果你发现特殊逻辑容易实现需求,否则很难。那么,你的架构已经出现问题了,你和你的团队应该深入思考这个问题,而不是轻易加上一个特殊逻辑。

务实一点,在编码时就考虑怎么测试。不然,你永远没有机会考虑了。当面对着测试性低的代码,需要编写自动化测试的时候,你会感觉很难受。

一旦代码写出来,就要尽早开始测试。这些小鱼的恶心之处在于,它们很快就会变成巨大的食人鲨, 而捕捉鲨鱼则相当困难。所以我们要写单元测试,写很多单元测试。

我们应该面向业务模型编程。我在《Code Review 我都 CR 些什么》里也提到了这一点,但是我当时并没有给出应该怎么去设计业务模型的指导。我的潜台词就是,你还是仅仅能凭借自己的智力和经验,没有很多方法论工具。

DDD(Domain-Driven Design),面向领域驱动设计。

Google 的很多同学说(至少 hankzheng 这么说),软件工程=科学+艺术。

核心代码应该通过 DIP 来让它不要和具体框架绑定!它应该使用 DIP(比如代理类),抽象出一个防腐层,让自己的核心代码免于腐坏。

选择一个框架,你不去做防腐层(主要通过 DIP),你就是单方面领了结婚证,你只有义务,没有权利。同学们要想明白。同学们应该对框架本身是否优秀,是否足够组件化,它本身能否在项目里做到可插拔,做出思考和设计。

UNIX 编程哲学告诉我们: 如果有一些参数是可变的,我们应该使用配置,而不是把参数写死在代码里。

配置即隐性耦合。配置只有和使用配置的代码组合使用,它才能完成它的工作。它是通过把'一个事情分开两个步骤'来换取动态性。换句话说,它让两个相隔十万八千里的地方产生了耦合!作为工程师,你一开始就要理解双倍的复杂度。配置如何使用、配置的处理程序会如何解读配置。

代码能够有很强的自解释能力,工程师们更愿意阅读可读性强的代码,而不是编写得很烂的配置文档。配置只能通过厚重的配置说明书去解释。当你缺乏完备的配置说明书,配置变成了地狱。

需要在代码里 import 插件包。

需要在配置文件里配置插件参数。

按层封装

按功能封装

按领域封装

按组件封装

留下 TODO 是很简单的事情,我们为什么不做呢?

即'错误传递原则'。这里给它换个名字—你不应该主动把很多有用的信息给丢弃了。

在 Google,自动化测试是硬性要求在限定时间内跑完的。

是个简单的道理,和'所有细节都应该被显式处理'一脉相承。

减少耦合是我们保障代码质量的重要手段。请把 ETC 原则放在自己的头上漂浮着,时刻带着它思考, 不要懒惰。熟能生巧,它并不会成为心智负担。反而常常会在你做决策的时候帮你快速找到方向,提升决策速度。

上一篇
下一篇