2009年1月6日星期二

Good Code,15年的转变

如何写Good Code? 15年的转变


前不久刚刚读了一本新书, 《Clean Code》 (Robert C. Martin, Prentice Hall, Aug 2008 ) (我的读书笔记)。里面讲到了什么是good code,什么是bad code。


刚好同事手中有一本翻译的电子书《编程精粹》,打开来一看,英文名也叫做Writing Clean Code。上网核对了一下,其实原书并不叫这个名字,而是Writing Solid Code: Microsoft's Techniques for Developing Bug-Free C Programs (Microsoft Press, May 1993) 可能是Clean Code比Solid Code更时髦吧,翻译的人不知是不是有意的改了原书的名字。







从May 1993到Aug 2008,十五年另三个月,什么东西变了,什么东西还没变?







1993年,面向对象的方法仍在萌芽期,那时还是面向过程语言的天下,尤其是C语言。“高内聚,低耦合 ”的设计理念虽然早已提出,但那时只是系统设计的原则,似乎与代码并没有什么关系。因此,那时的书中更多的是从代码的正确性、可读性、稳定性等方面去描述“good code”。







命名:早期C语言比较流行的匈牙利命名法甚至一直延用到C++中,我不知道现在的VC程序员是不是仍是采用这种方法。可能一方面因为老的编程语言把标识符长度限制得很短,另一方面C不是强类型语言,所以在名字中要用pch之类的前缀来表示这是一个指向字符的指针。现在不同了,标识符长度并不是问题,类型检查也不是问题。现在的命名更注重清楚、明确地表达名字所代表的逻辑含意,可以让读代码的人不依靠注释或或文档就能轻松的了解设计者的意图。Robert C. Martin甚至固执的要求这些名字一定要是可以拼读出声,并且由有含意的单词组成的。







单元测试:1993年的书上强调了单元测试的重要性,但作者也提到:“......因为尽管单元测试也与无错代码的编写有关,但它实际上属于另一个不同的类别,即如何为程序编写测试程序。......”那时的观点是:如果有单元测试的话,一定要在修改程序之前运行单元测试。而在2008年的书上单元测试早已成了编写代码密不可分的一部分。TDD或测试驱动开发慢慢的成为了一种标准。并且,有“控制反转 ”这样的方法帮助写出可测性更强的代码。






Lint:代码静态检查工具。在1993年的书中不断的被强调,而在2008年的书中却很少提及。Lint早成为编译器的一部分。







调试:相信对于代码走读的重要性在这些年里并没有太大的变化,只不过现在有了peer review和pair programming这些新名词而已。不过这些在2008年的这本书中并没有很多提及。不同的是在1993年的书中除了代码走读之外,还花了很大的篇幅来讲解代码的调试和单步跟踪技术。这在2008年书中是不被鼓励的,因为程序会有单元测试来保证,一但有问题会被随时发现并且精确定位,要尽量减少浪费在调试跟踪上的时间。还记得在Python“八荣八耻” 里提到:“......以打印日志为荣 , 以单步跟踪为耻;......以单元测试为荣 , 以人工测试为耻;......”









If:1993年的书上提到“避免无关紧要的if语句”,显然这是为了降低代码的复杂度,然而书中并没有给出切实可行的替代方法。而在2008年的书中提出了很多if甚至switch都可以用类的多态来替代。不禁又让我想起了Python“八荣八耻”:“......以多态应用为荣 , 以分支判断为耻;......”







断言与错误处理:在1993年的书中强调对“无定义”情况的断言及对错误情况的处理。而在2008年的书中却鼓励用“exception”来代替错误处理。这样可以避免影响代码的主逻辑。







函数:1993年的书中要求:“不要编写多种功能集于一身的函数。为了对参数进行更强的确认,要编写功能单一的函数”以及“要避免布尔参数”等。这与2008年的书中的观点是一致的。不同的是,除了“一个函数只做一件事”之外,作者还要求我们在一个函数中“只对一个层次进行抽像”等等。








易读性:1993年的书中要求“为一般水平的程序员编写代码”,对于这一点以及其它易读性相关的要求在2008年的书中只有更多,更高。

重复:在1993年的书中并没有提及“重复”在代码中的危害性。而在2008年的这本书中,DRY(不要重复)这个极限编程的原则不断的被强调。甚至声称它是一切代码问题的根源。







整理代码:1993年的书中提到“除非关系产品的成败,否则不要整理代码”。这条观点在2008年的书中要一分为二的来看了。基本上两者的观点是冲突的,因为有了完整的单元测试作保证,加上相关的技术,使得对代码的不断重构成为可能,但这并不是一本关于重构的书。另一方面,2008年的书中提到的“开关原则”讲到:“软件实体(类、模块、函数等)应当对于扩展开放,对于修改关闭”相信是系统设计的原则,与前者的提法无关。不得不提一下2008年书中提及的童子军格言:“离开时,让露营地比我们来之前更干净。”






过份设计:在1993年的书中就已经提到了过份设计的危害,但只是在代码层面上。它提到:“不要实现没有战略意义的特征”,“不设自由特征”,“不允许没有必要的灵活性”。受到lean principle的影响,在2008年的书中有更具体的描述,并提出要避免BDUF(Big Design UpFront)。这不单单只是一个编码原则,而是一个系统设计原则了。






试一试:在1993年的书中要求“在找到正确的解法之前,不要一味地“试”,要花时间寻求正确的解”,有时间多去读读文档。而这个与2008年书中的观点大有不同。2008年的书中强调建立系统边界,并且编写测试代码去测试这些边界外的行为。






数据抽象:在1993年的书中提醒我们要密切关观数据流,因为它是程序的命脉。而在面向对象的2008年的书中更多的是讲数据抽象。并且强调迪米特法则






系统设计前面提到了,1993年的书只是在代码级别讨论。而面向对象和面向方面(Aspect-Oriented Programming )的方法给2008年这本书提供了更多可以为good code做的事情。书中更强调降低模块(如类)间的耦合性,提高内聚性。并且书中专门写到了一些系统设计的“关注点分离”原则。它提到:“软件系统与物理系统不同。如果我们可以恰当的分离关注点,软件系统的架构可以增量成长。”





1993年的Writing Solid Code2008年的Clean Code
侧重点:代码的细节好的设计
 
命名匈牙利命名法描述性,可以读出声
静态检查一定要用没强调
调试强调调试与跟踪的技术避免调试代码
IF/ELSE避免无关紧要的if语句使用多态
错误处理要面面俱到使用exception
函数不要写多种功能于一身的函数只做一件事,只在一个层次进行抽象
易读性为一般水平的程序员写代码小的就是美的,用代码代替注释
重复(copy-paste)没提及是一切代码问题的根源
单元测试一定要写TDD、控制反转
整理代码除非关系产品的成败,否则不要整理代码“离开时,让露营地比我们来之前更干净。”
过份设计不要没有意义的特性TDD
数据抽象数据流迪米特法则
系统设计不是重点关注点分离


OO使得代码设计与系统设计更好的结合,所以2008年的书中在新的good code的定义中加入了更多对系统架构的要求。随着Aspect Oriented Programming以及系统TDD的出现,我们可以看到代码设计的将会越来越靠近实际的业务逻辑,远离琐碎的底层细节。




没有评论:

发表评论