首页 > 代码库 > 再转次梁子谦的文章 - 职责分离的编码操作手册
再转次梁子谦的文章 - 职责分离的编码操作手册
职责分离的编码操作手册
本次codereview发现的问题大多是已经被前几期点评提到过,于是重新读了一下被review的代码,发现了一个平时大家不是很在意的问题:从代码文件的长度上,反映出来关于控制编码过程复杂度的习惯。
这次review的代码有看到class定义就超过300行的案例,这类代码或许大家平时也可以理解,因为很多功能聚集在一起,并且相互依赖,例如大部分游戏都有的player class,功能实在太多,甚至有上千行的class 定义,player.h include了整个项目中50%以上的模块头文件,并被大部分cpp文件include,构成了调用结构上的大热点。这种源码想必大家都可以想象的出来,维护起来非常非常的困难,在脑海中构建一个该模块的分析视图,是多么痛苦。
于是想到趁着这次代码点评的机会,翻出来去年写的一个小品文,跟大家分享一些个人的编程观念和习惯,通过更深入的设计工作,达成相对合理的职责切分粒度,并使用构思型注释+翻译型编码过程,增强开发的准确和可控性。
这里列举一些软工行业bible或流传已久的观点,作为后续讨论的铺垫:
1 我们经常听到工程师说,工作效率提升的瓶颈在于无法专注于编码工作,工作时经常被谈话,会议,rtx等不定时插入的事件打断。
2 《人件》(peopleware)一书中有讲到:进入专注状态,需要15分钟连贯的工作,而一旦被打断退出专注状态,就像堆栈被清空,再次建立思维的缓存和上下文环境,需要另外的15分钟,在理想情况下。
3 在booch的OOA-OOD一书中提到:人脑中管理的事件不可过多,最好少于7件事,这样可以保证思考的质量,过于拥挤的思考目标,会让大脑的思考能力过载,有顾此失彼的风险。
4 我们经常听说一些优秀的geek说,“我最高效的时间在深夜23点到25点,1小时可以抵得上白天8小时,blahblah……”除了作息习惯生物钟方面的原因,也有在夜深人静的时候,干扰打断较少有很大的关系。
5 我们经常听一些老程序员说,笔误是无法避免的,只能靠编辑器的自动功能来降低,是这样吗?
——如上这些情况,作为一个程序员,我们如何来做?
一方面,我们寄希望于团队成员的沟通有效性,彼此尊重对方的工作时间,降低无意义的打断,提升合作伙伴综合素质与办公室礼仪,避免大声喊话,免提接打电话,以及rtx群聊等,但是正如您心中所想,这只是在理论上存在希望。
另一方面,我们从改变我们编码工作的工程性角度,提高我们的抗击打能力避免破防,提高韧性减少暴击。下面我们来看看,如何做可以接近这一目标。
我们把编码的工作分为好几个阶段,每个阶段尽可能专注于一个独立的工作任务、职责。
现在假设我们要完成一个模块(大到服务,小到数据结构,总之就是一个工作单位,称作模块),在正常情况下,我们假设工程师可以正确的完成(物理设计的部分今天我们不讨论了)这个模块的,只是过程的效率和最终产出物的质量有所不同。
三个基本层面包括:功能设计,实现规划,与翻译型coding。
1 模块的职责设计——这个模块在整体系统中的价值,为什么非要写这么一个东西不可,不写可以吗?这一步包含的工作还有,模块的上下游接口与界面行为,例如调用者与被调用是如何合作的?这些逻辑之间的时序关系如何?状态机的某些状态是否有冲突?等等。
2 模块内部的层次设计,例如接口层,逻辑层,核心数据层,对外依赖层,等等。这一步解决的问题还包括模块内部各子模块的可扩展性,各子模块间的逻辑依赖与调用关系依赖关系,是否需要中间层屏蔽扩展或依赖倒置?等等。
3 模块的核心数据结构,核心算法,技术难点如何实现的细节确认——anyway,我们知道在大多数系统中这部分挑战不会让工程师辗转难眠,如果不是这种情况,核心设计还有很大不确定性的话,请把这个步骤放在1号阶段之前,确认该模块的物理可行性。
4 合理粒度的职责切分,即头文件。并在头文件中使用注释明确下来这个子模块的上下游合作者(其他子模块),大家是如何互动起来共同完成工作的,等等。
5 .c(.cpp)文件的函数名和构思型注释。这一步是重点、关键,先写构思型注释再写代码。在注释中用程序员最熟悉的自然语言写入分支,循环,大括号、段落、时序等,在编写注释时,其实我们考虑的健壮性,容错,边界,分支,循环条件,数据类型,资源的分配与释放等等如何让计算机按照我们的意图完成工作的核心价值。这一步决定了计算机将要做什么,如何做,构思型注释可以保证编码者使用最自然最符合自己思维习惯的方式来思考,并表达构思的成果。
6 翻译型编码。将函数实现中的人类语言注释翻译成c/c++/java/c#/python/lua/lisp/.sh等各种编程语言,翻译工作无疑是需要我们一些语法运用的技巧,变量名函数名和外部库函数的信息,现在把它们都转化为编码者的语言,和作品。保守估计,这部分也要占据我们稀有的七件事中的一半多,那么在做翻译工作的同时,如果我们还在做流程或资源管理接口设计健壮性陷阱规避的话,我们的头脑真的是太可怜了,它很可能不堪重负(尤其是做比如大型游戏这样极为复杂逻辑的时候)。避免这种风险,让复杂的事情在构思中完成,让表述这件本身已经很复杂的事情在翻译型编码中完成。
综上,1-4是设计,5是实现规划构思,6是翻译编码,如果其他都不做,但是能坚持把5和6分开,代码质量(我猜:-)也会有明显的提升,让自己的工作简单起来,准确起来!
补充:在hacker的工作方法中,是比较推崇灵感和即兴发挥,在编码的同时完成绝妙的设计,但是与大型项目的工程师不同,hacker考虑的是作品的功能,乐趣,而不用在脑海中始终警戒“不够强壮的一百个理由”“会把上下游模块带进沟里的两百个陷阱”“行行都清晰可理解的三百行代码”,同时hacker的编码时段、地点与正常工程师也稍有不同,不多讨论。
另外推荐读物包括以上提到的peopleware,OOA-OOD,与一个很有意思的小册子:番茄工作法图解。