本篇文章是《程序员修炼之道》第二章的笔记,总结了高效程序员需要遵守的一些原则和常用的开发模式,对我们有非常重要的指导意义。建议每个程序员都应该学习并掌握这些原则。如果大家觉得这个系列文章有价值,我们可以组织一次抽书的活动,鼓励大家从原文学习。
DRY 原则
软件开发过程无时无刻都伴随着维护,如果项目中有大量的重复代码会对我们的维护工作造成很大的麻烦。比如:一段代码在多个地方出现, 一旦要修改就需要我们同时修改多个地方,如果某个地方忘记修改就可能导致一些不必要的错误。因此作者提出 DRY原则 - Don't repeat yourself(不要重复你自己)。
我们平时有不少重复的场景, 同时也有避免重复的解决方法,下面举几个例子。
- 代码与注释:我们经常被说教要在代码里加注释。但注释并不是越多越好,我们应该把注释留给高级的说明,对于比较低级的说明用代码代替注释即可。否则,就存在重复的知识,每次修改代码都要想着修改注释
- 代码与文档:文档的更新是永远滞后于代码的,经常更新了代码但忘记更新文档。我们可以采用一些辅助技术或者自研的技术,比如:API doc、Java doc 之类的工具,代码更新后,文档会随着更新
- 设计中的重复:假设某个类中有个起始坐标和结束坐标两个属性,这时再加一个两点间距离的属性就有点重复了,因为我们可以根据起止点计算出距离
- 临时性的重复:有时候我们临时做一些需求可能用到了项目中某段代码,我们一般会直接复制过来用,这时候也存在重复。更彻底的做法是我们可以把一些比较常用的计算逻辑抽象成接口,形成自己的工具包,需要的时候直接引用即可,无需拷贝代码。比如:一些常见的求和,求最值等需要 for 循环的处理逻辑是不是可以抽象一个 reduce 函数
- 开发者之间的重复:这个是比较常见的重复,如果一个项目的两个组同时开发很容易造成同一个小的逻辑在两个组的成员有各自的实现,这种重复不太好避免。但我们可以增加团队之间的交流,可以组织一个平台,把一些比较好的组件开放出来。我做数据开发时经常会用到一些 udf 处理数据,如果我当前项目中没有,我会到公司的公共平台去搜相应的关键词, 一般都能找到现成的 udf。我比较建议团队内部开源不同的项目代码,当然是不涉及机密代码的前提。我在开发中经常会问上游同学数据怎么处理的,经过了什么逻辑,这样做一方面增加沟通成本,二来毕竟耳听为虚。所以我就经常反编译上游的代码,以后需要看逻辑不需要问别人,直接看代码节省非常多的时间。当然开源也有个好处是我们可以学习别人代码中优秀的地方。
正交原则
正交性就是不相互依赖性或者解耦性。如果两个或多个事物中的一个发生变化,不会影响其他事物,那么这些事物就是正交的。对应到编程,就是我们常说的高内聚、低耦合。正交的系统好处非常多,正交的系统可以降低风险,当系统中某个组件修改时,只要对外的接口保持不变,该组件就可以任意修改而整个系统不会受到影响。正交的系统能提高生产率,因为系统组件是解耦的,因此各个组件可以独立地、并行地进行开发。比如最近几年比较火的前后端分离技术就是很好的代表,以前后端的同学经常需要写一些套页面的前端代码,前后端同学的代码交织在一起相互依赖。但前后端分离后,只需要把协议确定好,前后端技术就可以解耦,开发同学就可以并行开发,提高生产率。下面列举几种维持正交性的方法
- 设计:设计系统时我们可以采用基于组件的分层架构。每层提供一级抽象,每层只是用其下面的层次提供的抽象,层与层之间的协议/接口稳定且可扩展,下层组件的改动对上层透明
- 编码:为了让代码保持解构,我们可以开发 “羞怯” 的代码,对于不需要开放出来的逻辑,可以控制其访问权限,不被其他组件引用。避免使用全局数据,全局数据涉及多个使用方同时更新,导致组件耦合度较高。避免编写相似的函数,相似的函数往往有重复的代码,使得修改代码要同时更新多处
- 测试:在进行单元测试时,如果某个单元牵扯系统其余很大一部分,说明该单元与系统其他部分耦合度较大,需要引起开发同学的重视
可撤销原则
可撤销原则是说,当系统中某个部分需要改变时,我们能不能很好的撤销已有的代码,灵活的适应新变化。因为需求无时无刻都在变化,因此可撤销性就一直存在。举个栗子:假设我们开发一个数据库可视化软件,最开始的需求是基于 Mysql 的,如果我们不做分层设计,凡是需要增删改查的地方我们直接写 JDBC 代码。但是某一天我们底层数据要支持 MongoDB 怎么办,因为我们的 JDBC 代码分布在项目的各个模块的代码中,几乎无法撤销,这时候系统只能重写。如果我们设计之初将数据访问层作为一个组件抽象出来,那么上层只需要调用抽象出来的接口来操作数据库。这样做的好处是当需要支持其他数据库时,只需要为该数据库编写支持我们抽象接口的数据库访问代码即可,因此系统就具备可撤销性的。其实,我们在 Web 项目中经常使用 ORM 框架也是基于可撤销原则的,ORM 可以将数据库表映射成对象,底层的增删改查对上层透明,所以可以灵活地调整底层数据库。
曳光弹与原型
在黑暗中需要打击军事目标时通常会使用曳光弹,它在枪与击中的地方之间留下一条烟火般的踪迹,用来指示弹道和目标,从而协助射手修正弹道。其实,黑暗中的目标就像我们开发中面对的未知系统。面对未知系统,如果我们制作大量文档,逐一列出每项需求,尝试确定所有未知因素,就犹如在黑夜中对目标未知预先进行大量计算然后射击,很显然这种情况需要消耗大量计算力并且不一定能击中目标。而注重实效的程序员往往更喜欢使用曳光弹。曳光弹的核心优势就是反馈是及时的。比如:我们要开发一个支持多种序列化格式的 RPC 框架,最开始我们是不是可以先用简单的系统默认的序列化方式先将整个系统框架搭建起来,先让系统能够运行起来。运行后我们可以及时地得到使用方的反馈,修复已有问题、增加多种序列化框架、不断迭代完善。
介绍完了曳光弹再来说说原型,记得在大学学习《软件工程》时就接触了原型开发。原型更像是一个 Demo,不注重代码的实现,而是能够快速出一个能够与产品确定需求的东西。比如:我们需要确定某个系统的 UI 需求,我们可以用最快的开发语言,开发出一个 Demo,它的代码不需要规范,交互界面也不需要太美观,因为原型大概率不会在后续实际的项目实现中使用。
接下来简单总结一下这两种开发模式的区别。曳光弹强调的是明确需求后,我们能不能开发出一个麻雀虽小、但五脏俱全的系统来快速获得反馈,从而指导我们进一步迭代。在曳光弹开发模式下,后续代码是依赖于第一版的代码。而原型开发更侧重明确需求这个阶段,快速开发一个 Demo ,目的是为了能够基于原型确定系统的需求。这个阶段不在乎用什么代码、不在乎系统的完整性、健壮性以及正确性,因为原型代码基本不会用在真实的系统开发中。
领域语言与估算
这节内容我觉得平时应用不多,理解的不深刻,因此就简单总结。领域语言我的理解就是使用(创造)一门规范的伪代码,为什么是伪代码呢?假设我们与产品沟通需求,我们可以使用文字,但我们都知道中华文字博大精深,别人表达的意思跟我们的理解可能不一致,否则的话我们也不需要这么苦逼的加班,当然直接用编程语言沟通更不可行。那么就需要一个中间层的伪代码,既能清晰的表达出需求,又能让产品或者使用不同编程语言的程序员都能看懂。
对于估算这一节,作者介绍了一些常见的预估问题(估算项目时间、流量)的一些指导性意见,但感觉比较偏理论,实操性不强。但给我印象最深的一句话是:在咖啡机旁给出的估算将像咖啡一样回来纠缠你。也就是说估算不等于不假思索的回答,我们可以用 “这个问题我现在答复不了,我回去想下” 之类的话术先应付一下,后续再详细地思考。
小结
本章主要介绍了三个原则中,这三个原则如果在开发中加以总结和利用将对我们高效地开发非常有帮助。DRY 原则避免重复劳动,正交原则避免改动一个组件时牵扯整个系统的维护,可撤销原则避免更换系统某个组件时导致系统崩溃。最后,介绍了曳光弹和原型开发两种快速开发模式,尤其是曳光弹,我比较喜欢用,先小规模、小成本搭建骨架并跑起来,后续再不断地丰富骨肉,希望以上介绍的内容对你日后开发有帮助。
欢迎关注公众号「渡码」,我将分享更多优秀书籍的内容,组织定期抽书活动