[TL] {技术}{讨论}十多年来,对什么是好代码定义的变化

4 views
Skip to first unread message

Terry

unread,
May 21, 2010, 10:26:54 AM5/21/10
to TopLanguage
其实同一篇东西,以前我在其它地方也发过。这两天翻了翻几年以来写过的东西,忽然觉得这个可以改一改在TopLanguage里也发一下。看看是不是有
人也象我一样注意到了这么多年里人们对“好代码”理解的演进。也许再过十五年,人们对“好代码”的定义和今天又全然不同了。谁知道呢,也许不用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 Code
2008年的Clean Code
--------------------------------------------------------------------------------------------------------------
侧重点: 代码的细节 好的设计

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

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

meta

unread,
May 21, 2010, 10:44:50 AM5/21/10
to pon...@googlegroups.com
优秀代码的原则: 正确优于性能,简单优于复杂,清晰优于技巧。

2010/5/21 Terry <terry....@gmail.com>

Terry Yin

unread,
May 21, 2010, 10:51:19 AM5/21/10
to pon...@googlegroups.com
精辟!其实对于优秀代码的基本原则并没有变。

2010/5/21 meta <carma...@gmail.com>



--
-------------------------
Blog: http://terry-yinzhe.spaces.live.com/
twitter: http://twitter.com/terryyin

AWu

unread,
May 21, 2010, 12:27:03 PM5/21/10
to TopLanguage
mark~有时间一定要去读clean code~现在读完code coplete先~~~

开始狂怨念之前在某公司做过的事情,调试的代码,满屏幕的if else,else if~没完没了的debug n n s s~~~

Galaxy

unread,
May 22, 2010, 1:21:35 AM5/22/10
to pon...@googlegroups.com
对重复代码,有一种情况是:
单线程下,对一个耗时的循环,根据参数分别采取是否显示进度的方案,如果想省掉那个if,就直接写两个版本。
前阵子看makefile,感觉可以通过conditional causes处理两个函数名和显示进度的部分,用不同变量把同一个c文件编译两次,再链接到一起。

这有比较好的处理方法吗?
想到如果是多线程的,要显示进度就再开个线程去定时读。

Terry Yin

unread,
May 24, 2010, 8:04:27 AM5/24/10
to pon...@googlegroups.com
这种情况下,函数指针也是个开销很小的方案。C语言的函数指针风险很高,所以大家都尽量避免使用。但是如果合理使用其实会比同一个C文件用不同编译开关编两次更加清楚一点。

2010/5/22 Galaxy <gala...@gmail.com>
Reply all
Reply to author
Forward
0 new messages