本文转自:http://blog.chinaunix.net/uid-13164110-id-3410985.html
编程匠艺——编写卓越的代码
[美]Pete Goodliffe(皮特.古德利弗)著
韩江 译
再序
这本书能够重印,本身就说明了它的价值获得了读者的认可,在今时今日,已属不易。所以时隔三年,我虽然已经不在原来的工作岗位上,也还愿意再就这本书说几句。
这本书讲了什么呢?其实也没什么屠龙秘笈,无非还是关于软件编码的一些普普通通的大实话,一些实实在在的经验之谈。它的优点,一是比较完整,二是具体而又简明,三是朴实。总的来说,我觉得这本书比较好地概括了软件开发当中真正行之有效的东西,而且不包含花哨的噱头。这是难能可贵的。
软件开发行业还没有完全走出青春期,离成熟还远。行业跟人一样,所谓成熟,就是说试过了很多想法,狂也狂过了,疯也疯过了,痛定思痛,返璞归真的那么一种状态。软件开发这个行业,给狂想提供了特别大的舞台。每个程序员都希望成为高手,而高手似乎就要体现在各种各样炫酷的代码、框架、程序库上面,所以各种看上去很酷的想法层出不穷,程序员们周期性地为最新的时髦词汇而痴狂。但是这么多年的实践,很多人其实还是看明白了,大多数酷炫绝技最后身败名裂,尘归尘土归土,留下来经久不衰的,还是那些低调、朴实、透明的技术。这本书里讲述的,大多数属于长期沉淀结晶出来的东西,所以值得信任。
几年前,我参加一个技术会议,一位发言人是程序员出身的天使投资人,他在评价另一个靠技术成功的年轻人时说,那个某某某,其实技术很平庸,但是他为什么成功?因为他做出了一流的产品,而且是个商业天才,所以成功了。很有意思的一段话,做出了一流的产品,技术水平还能叫“平庸”吗?软件开发行业有一个特别热闹而且旁若无人的圈子文化,大多数开发者都在圈子里不亦乐乎,不是拿成果,而是拿点子来比高下,把大部分精力用来取悦同行。不过我提倡开发者经常站在圈子外面审视自己,这样会比较容易保持头脑的清醒。归根结底,社会还是以你是否能交付高质量的、易于维护和扩展的产品来评价你的能力和水平,并且给予你相应的待遇。所以开发者到底应该怎样规划自己的发展路径,其实是值得反思的。
我觉得这本书好就好在没多少花花架子,都是实实在在的好东西。所以即使是三年以后,五年以后,也还值得再读。
孟 岩
2011年7月于北京
推荐序
信息产业几乎是开放程度最高的产业,其发展之快,让身处其中的我们既感到兴奋和激动,也时常困惑和惶恐。信息技术的变化太快、太激烈,一切都在迅速革新当中,作为技术人员,我们今天熟悉并且赖以安身立命的东西,很快就会变成故纸堆里的古董。人生总是需要不断积累才能越走越高的,如果找不到一项可以积累提升,而且被市场所承认的核心竞争力,事业的大厦是建不起来的。于是一个严峻的问题就摆在每一个技术人员的面前——什么才是我们的核心竞争力?
二十年前,人们认为软件开发者的核心竞争力应该体现在他的聪明才智和扎实的基本功上,要精通算法、硬件、编译、计算机体系结构,要有天马行空般的想像力和创造力,能够单枪匹马单打独斗。至于什么团队协作、编码规范、单元测试、最佳实践,都是对天才的不必要的羁绊。
大约到了20世纪90年代中期,风水变了。随着软件的规模越来越大,以及当代操作系统、面向对象技术、软件开发工具、程序库和框架的普及,“平台”和“工具”变得越来越重要了。大多数时候,开发者不需要自己从头实现所有功能,他所依赖的平台已经准备好了现成的组件供使用,有的时候甚至应用程序中最繁琐、最复杂的部分已经由框架或程序库实现好了。因此,人们普遍认为,一个开发者的核心竞争力很大程度上表现为对平台与工具的理解和掌握,并依托这种理解创造性地构筑产品,或者更确切地说,一种创造性地组装产品的能力。你还在为刚刚琢磨出来的一个高效算法而得意吗?旁边那位借助程序库里提供的组件,已经快速地完成了整个功能模块;你发明了一个新的视频格式以及对应的codec?很了不起!不过另一个初出茅庐、连快速傅里叶变换算法都写不出来的团队已经基于sourceforge上的开源组件做出产品来,并且播得满互联网都是了。当然,黑客们仍然有着不可取代的价值,总是能找到自己的位置;但是对于那些强调团队作战、快速行动的企业来说,一个熟练掌握MFC、Delphi、J2EE、ASP.NET或者LAMP的开发者可能更实用,能更有效地为企业带来价值。因此,这样的程序员便一时成为企业的宠儿,众人眼中的高手。
然而不到十年下来,问题又出现了。流行的平台和工具如走马灯般你方唱罢我登场:昨天还在为领悟了MFC、Delphi而沾沾自喜,今天就发现应用主流已经是Web了;刚刚啃完艰深的EJB2,抬眼一看却发现它已经被Spring的拥趸们批倒批臭了;上个月还是冲在敏捷Java领域的改革派,这个月就被一群嘴上无毛的RoR粉丝给划到改革的对立面去了;ASP.NET还没学踏实呢,微软又准备好ASP.NET MVC、ASP.NET AJAX、Silverlight等等一大堆新玩意让你啃了。这样下去,什么时候是个头?把自己的核心竞争力建立在这些转瞬即逝的昙花上,难道不是把有限的生命投入到无限的瞎折腾之中吗?难道只有钻到一间舒舒服服的大公司里,到了三十多岁就寻求所谓的“转型”,顺着一条十分确凿的“职场路线”攀或是混,最后在公司没有倒闭或者自己没有被“战略裁员”的幸运之下头顶玻璃天花板光荣退休,才是中国程序员的归宿?什么才是程序员可以长期积累,不断提高,不但足以安身立命,而且能够实现梦想、成就事业的核心竞争力呢?回答好这个问题,对于今天的开发者来说,可能比掌握和精通某项具体技术意义重大得多。
在我看来,当代程序员的核心竞争力至少应该体现在这么几点上:有扎实的基本功,活跃的想像力与创造力,快速的学习能力,具备行业和领域知识,以及专业的软件工艺能力。而在这其中,专业软件技能是最基本、也是最重要的一项。
什么是专业软件技能呢?就是正确地开发软件的能力,更具体地说,是通过一系列有组织的、有原则、流程化、可检验、可重复的实践行为,协作式开发高质量程序的能力。对于一个程序员来说,这是你的看家老本,对于一个软件团队来说,这是你们的立足之基。算法不会,可以查资料慢慢掌握;不理解行业,可以边做边学,逐渐深入;缺乏创新,可以站在巨人肩膀上耐心摸索;甚至基本功不足,也可以自我弥补,可是如果没有做软件的专业态度和实践技能,没有制作合格软件的工艺水平,连一段高质量的程序都写不出来,试问你还剩下什么?
经过近三十年的时间,人们最终认识到,在规模化团队协作的情况下,决定软件产品质量的不再是个人的聪明才智,也不是靠什么神仙技术,而是团队的工艺实践。是否在一开始就形成了开发计划?是否对这个计划进行了必要的确认、维护和跟踪?必要的规范文档是否撰写了?是否形成了合理的架构?是否恰当地选择了开发工具和编程语言?是否建构了适于团队渐进协作的良好的工具和工作平台?是否一开始就形成了有力的缺陷核查、控制和跟踪策略并始终严格地执行?是否制定了连续一致的编码标准,并且通过诸如代码走查等加以保证?是否有完整的测试制度?是否具有明确的性能优化和软件安全性保障过程?是否在整个生命周期贯彻了严格的版本管理、配置管理、发布管理和软件维护退役管理措施?这些实实在在的问题,是需要耐心与细心地用具体实践细节来回答的。当一个团队对于这些问题都给出了明确而一致的回答并且用行动来执行的时候,他们就是一个专业的、具有核心竞争力的团队。而当一个个体开发者能够对这些问题具备正确的观念,并且通过施加自己的影响力促进团队向正确的方向前进的时候,他就是一个具有核心竞争力的开发者。一个具有核心竞争力的团队和开发者,是可以不断进步的,是具备把握机遇的能力的;一旦时机合适,他们就完全有可能实现更大的目标。
十多年以前国内外软件界对工艺的问题并不重视。大部分人要么执迷于技术本身,指望某一天一个面向某某的技术能够一劳永逸地解决软件开发中的所有问题,要么就是把问题大而化之为“软件工程”,企图以指令性的方式,在宏观的层面上用管理取代工艺。在这两个方向上,程序员要么被视为可以充分放纵的孤胆英雄,要么被视为伟大编程技术最终出现之前不得不存在的过渡品,或者管理指令的机械的执行体,“人”的维度消失了。这种对于人和工艺细节的忽视也体现在技术著作方面。软件工程、面向对象、编程技巧和产品手册之类的著作汗牛充栋,而认真谈到软件工艺的书屈指可数。
直到20世纪90年代中期,随着一些软件产品的规模越来越大,微软率先认识到工艺问题的重要性,于是出版了诸如《代码大全》、《编写清晰的代码》等一系列探讨这一问题的著作。直到20世纪90年代末期,当整个工业界从面向对象和软件工程的幻影泡沫中走出来之后,才开始认真全面地审视软件工艺的问题,而且通过敏捷运动、把软件工艺的重要性和基本实践提到了一个令人瞩目的位置上。事实上,敏捷运动可以认为是软件工艺的复兴运动。此外,随着《代码大全2》、《软件工艺》、《代码阅读》、《程序员修炼之道》等经典作品的出版,在技术图书领域也陆续出现了一批专门探讨软件工艺的著作。这本《编程匠艺》也是这个领域中的一本佳作。
本书是一部全面讨论软件构造工艺实践的著作,从软件开发的计划到架构设计,从编码风格规范到软件缺陷的检测与管理,从程序员工具箱的配备到团队协作精神的塑造,这本书都给予了翔实、风趣而具有启发性的讨论。这些讨论,既有原则性、理论性一面,也有技术性的具体建议,对于团队领导者、高级开发者和每一个希望快速进步的程序员具有明确的指导意义。如果读者认同软件工艺的重要性,那么可以说这本书是帮助读者建构自己核心竞争力的一本难得的作品。特别值得一提的是,这本书中文版的翻译流畅自然,在很多地方都体现出译者的认真态度和翻译功力。对于一本翻译自英文的技术著作来说,这无疑是一个大大的加分。
当然,一本书的覆盖面和功效毕竟是有限的,核心竞争力的确立和建构归根到底是一个艰苦实践的过程,不同性格的人也一定有着不同的目标和方式。但是我相信,对于有心人来说,只要我们不断地探索和实践,都会获得自己的核心竞争力,做一个有准备的人,争取和等待机会的垂青,最终实现自己的人生目标。
读此书有感而发,借题发挥,是为评论。
孟 岩
《程序员》杂志技术主编
2008年7月于北京
译者序
作为从事软件开发的程序员,你肯定遇到过这样的情况:自认为完美的代码,在项目快要结束的时候,却总是会发现还有好多内容需要修改。更有甚者,由于人员的变动,那些他们遗留下来的“老代码”,作为时间留给程序员与项目组的最大遗产,却可能会成为项目组的灾难。
除了受制于人类自身的缺陷之外,还有由于组织而带来的问题,如客户需求不断变更、必须在有限的时间和预算之内完成项目,来自内部所谓“项目管理”的种种压力,等等。天哪,这些问题我们绝大部分人都赶上了。
列宁曾在监狱中写下了《怎么办?》,指导了俄国的十月革命。而在软件业,从一代宗师Frederick P. Brooks的《人月神话》开始,就在找“怎么办”这个“银弹”了。然而,“狼来了”在多次被喊出来后,已经很少有人相信了。我们必须承认,这些都是根本层面的问题,目前还不能得到解决。但是,本书的作者Pete Goodliffe认为,至少我们可以采取一些方式,减少一些开发上的痛苦。因为,除了开发,人生还有许多更为美好的事物在等着我们。我们这次也可以高喊“银弹来了”。没有最好,只有更好,谁知道这次不是真的呢?
著名国画大师齐白石在年轻的时候,曾经做过木匠。据说有一次他和师傅去给地主干活,在路上迎面走来另外一对木匠师徒。齐先生的师傅说,赶紧给别人让路。师徒俩站在路边,老师恭敬地目送那两人渐渐走远。齐白石不解,问师傅:同是木匠,你我师徒为什么要给他们让路。老师傅回头说:为什么?别人是做细活的,我们是做粗活的。
Pete Goodliffe在业界的年头快要超过好多人的年龄了,此君曾经涉猎多个领域、不同的编程语言以及多种架构,并且曾经在采用不相同流程的公司里从事开发。在本书中,他把多年压箱底的一些观念想法和技巧告诉了大家,这些都是时间与智慧的结合,相信无论是开发人员、项目经理甚至测试人员,都可以从中发现阿里巴巴开启金库的钥匙。
那么本书有什么特色呢?对于想了解内容的普通读者来说,本书至少有以下特点:
1.贴近实际 《编程匠艺——编写卓越的代码》是本书的书名,但也是作者的用心所在。人生有三个境界,最后一个就是“看山是山,看水是水”。这是废话吗?当然不是,作者对此给出了最好的解答。作为程序员,我们最喜欢争论不同工具、平台、方法之间的优劣。而作者却通过多年经验,力图告诉我们应该如何提高质量,并成为一名优秀的程序员。这些方法就像点石成金的手指,它们是方法论,而不是针对具体的工具或者平台的说教。我们现在所缺的,恰恰是这些能使自己更进一阶的手段,而不是那些特殊的技术细节。
2.内容丰富翔实 很少有一本书能涵盖如此多的领域,并且还如此扎实。作为一名程序员,我们可能永远无法达到完美。而需要处于一种持续不断地提高的状态,总会有更多的东西需要学习。那么下一步应该做什么呢?这里就有答案。
3.可作为“秘要心法” 本书不仅适合入门者,也适合需要提高的开发人员,以及那些想管理好所谓代码猴子的项目经理们。与《项目经理案头手册》一样,这本书也将成为每人的案头手册或者枕边书,可以作为应急或者提升的手段。如果以后碰到了问题,可以随时参阅相关的章节。
4.心态决定一切 这句话对吗?有了良好心态,不一定行,如果没有,肯定不行。我们常常羡慕于老外以四五十岁的年纪仍然能继续从事编程,为什么我们不行呢?可能不同的读者都会找到属于自己的答案!Pete Goodliffe具有宽阔的视野,扎实的基础,广泛的爱好,带有一种程序员应该具有的高雅和恬淡。这正是我们这个浮躁的时代中积极探索的一代程序员所不具备的。
最后禁不住要抱怨一下,作者Pete Goodliffe以他丰富的阅历和爱好,给译者带来了不小的麻烦,比如出于对于音乐的爱好,所有章节的标题都来自英国的歌曲名称。为了理解上的直观,我们在翻译的过程中采取的是“信达雅”中的“雅”,以保证国内读者能很快切入主题。本书每章开始和行文的过程中,作者都引用了历史上或者现在社会中一些名人的名言,这给翻译增加了不少的难度,但是由于贴切精辟,这些名言也可称为点睛之笔。尤为值得高兴的是,此君对我中华文化竟然也有一定的造诣,孔夫子和老子的哲理名言竟然多次出现,而且能够贴切地表达出这些圣人的思想对软件开发有哪些启示,这非常不简单,难为了作者,也着实难为了译者。从外国作者的笔下,让我们着实体会到了自己国家的文化源远流长。这从一个侧面也体现出东海西海,千圣一心。
此书给了我们一个快速成功进阶的好范例。我觉得它更像一个程序员的入门或者修行心法。从此入门,我们可以少走很多弯路。同时,我们也要争取像佛经中“般若波罗密”所讲的那样:大智慧到彼岸,最后连佛法也像渡河的筏子一样,成佛后立即丢弃。我更希望的是,看过此书的读者们,最后能够拍案而起,大声说:我可以了。
译 者
2007-12-2 于北京
前言
这就是《编程匠艺——编写卓越的代码》要达到的目的。这本书的内容也许从未有人向你传授过:如何“在现实世界中”正确地编程。《编程匠艺——编写卓越的代码》填补了教科书中所缺少的内容。的确,这本书讨论了优秀代码的技术细节和难点。但除此之外它还包括更多的内容:如何以正确的方式编写正确的代码。
这是什么意思呢?在现实世界中,编写优秀的代码有多重的含义:
— 编写在技术上优雅的代码
— 编写可维护的代码,让其他人也可以看得懂
— 理解并改写其他人所编写的杂乱的代码
— 与其他程序员良好地并肩工作
要成为一个编码高手,你需要所有这些能力(甚至更多)。
你必须理解代码那神秘的生命:当你输入代码之后,会发生什么?
你必须拥有审美的能力:区分优美的代码和丑陋的代码。
你还必须在实践中运用理论的头脑:认真思索并解决以下问题,什么时候使用简化的操作是合理的,什么时候开始着手代码设计,以及什么时候停止和继续(实用的“提前退出”原则)等。
本书将帮助你实现这些目标。
你将学习到如何在软件工厂中求得生存,如何观察战场并了解敌人,如何制定战术以避开敌人的陷阱,以及如何在种种困难中开发真正出色的程序。
软件开发是一个有趣的职业。这个职业在迅猛地发展,充满了瞬间即逝的流行元素与变幻莫测的风尚、致富的计划以及新理念的传播者。
它并不成熟。我不是宣称我有什么神奇的答案,但我的确有一些来自经验的、有用的建议与大家分享。
本书没有象牙塔中的理论,而仅仅是现实世界的经验和一些好的习惯。
一旦消化了这本书的内容,你将不仅仅会成为一名更好的程序员,而且你将成为软件工厂中一位更优秀的常住居民,一名真正的代码战士,你将学到编程匠艺的技艺。
如果这听起来并不令人激动,那么也许你应该考虑在军队里谋职了。
做得更好
那么,是什么将“优秀”的程序员和“糟糕”的程序员区分开的呢?更重要的是,是什么将“杰出”的程序员与仅仅是“够格”的程序员区分开的呢?
其秘密并不仅仅在于他们的技术能力——我曾经见过很多对语言标准了然于心、能够写出非常紧凑和让人欣赏的C++程序的程序员,他们才华横溢,但是他们写出来的代码却非常糟糕。
我也曾经见过更多谦逊的程序员,他们坚持编写非常朴素的代码,但是他们所编写的程序却非常优雅和缜密。
真正的区别是什么?好的程序设计来自于你的态度。而好的态度在于你了解职业化的方法,以及对编写最好的软件的不懈追求,而不管软件工厂的压力有多大。
态度就像一面透镜,我们通过它来看世界。这面透镜为我们的工作和行为增添了色彩。
优秀的代码需要由艺术大师精心编写,而不是由懒散的程序员随意地粗制滥造。通向优秀代码的道路是由良好的意愿铺就成的。
要成为杰出的程序员,我们必须学会从良好的意愿起步,培养积极的看法,并使这种健康的态度发扬光大。
在本书中,我们将看到如何做到这一点。书中包含了大量的主题,小至实用的代码编写问题,大到组织架构性问题。在所有这些主题中,我都着重强调了什么是正确的态度和方法。
态度——前进的角度
面对软件开发的世界,随着调查和分类的深入,我越来越相信使杰出的程序员脱颖而出的是态度。字典中对“态度”(attitude)的定义是这样的:
态度
1.心态,看法:心理或感情的状态;性情。
2.飞机姿势,姿态:飞机轴相对于某一参照直线或平面(如地平线)所处
的位置。
第一个定义并没有什么令人意外之处,那么第二个呢?实际上,第二个定义比第一个更有揭示意义。
我们想像有三条贯穿飞机的轴线:一条从一侧的机翼到另一侧的机翼,一条从机头到机尾,还有一条与前两条轴线垂直相交于其交点处。飞行员靠这三条轴线来定位飞机,它们定义了飞机前行路线的角度。这称为飞机的飞行姿态(attitude)。如果飞机的飞行姿态不正确,那么只要有很小的外力施加到飞机上,飞机就会极大地偏离它的目的地。飞行员必须时刻注视飞机的飞行姿态,特别是在起飞和着落等关键时刻。
虽然这听起来像一个乏味的励志片,但它确实是我们的软件开发工作的一个贴切比喻。飞机的“态度”决定了它前进的角度,而我们的态度则决定了我们通往成功编码之路的方向。程序员的技术能力有多强并不重要,如果他或她的能力没有受到健康态度的支持,那么工作仍将是一件痛苦的差事。
错误的态度可以造成或中断一个软件项目,所以保持正确的前进角度来进行编程是至关重要的。你的态度要么阻碍,要么促进你的个人成长。要想成为更优秀的程序员,我们需要保证有正确的态度。
谁应该阅读这本书
显而易见,那些希望提高他们代码质量的人应该读这本书。我们都渴望成为更好的程序员;如果你没有这种渴望,那么这本书就不适合你。
你也许是一名职业的程序员,很可能已入行多年。
你也许是一名高年级的学生,熟知编程的概念,但是不太肯定应该如何将这些概念付诸于实践。
如果你正在接受指导,或正在指导某个新手,读这本书也会大有裨益。
你必须已经具有编程经验。本书并不是要教你如何编程,而是告诉你如何更好地编程。当我尽力避免语言的歧义和教条时,我需要举出一些代码示例。这些示例大部分是使用C、C++或Java语言编写的,因为这些语言是当今流行的语言体系。读这些例子并不需要你是某种语言的专家,所以即使你不是一流的C++程序员,也不要惶恐。
我假设你作为本书的读者,正在或者将要在软件工厂的压力下编写代码。这通常意味着你受雇于一个商业性的开发组织,但你有可能在开发一个混乱的开放源码开发项目,或成为一个受雇为第三方提供软件的枪手(项目承包方)。
本书包含哪些内容
本书的重点在于程序员的态度,但它绝不是一本心理学教科书。我们将深入探讨许多主题,包括:
— 源代码的样式
— 防御性的编码技巧
— 如何有效地调试程序
— 良好的团队合作技巧
— 管理你的源代码
快速地浏览一下目录,你就可以了解本书所包含的内容。选择这些主题的理由是什么呢?我曾有好多年从事培训新程序员的工作,在此期间这些主题反反复复地被提出。我也曾在软件工厂中任职多年,看到过很多一遍又一遍出现的问题,而这些问题都是与以上主题相关的。
如果你能够征服这些编程的羁绊,那你将从一个学习编程的新手成长为一位真正的编码高手。
本书的组织形式
我已经尽力将这本书写得易于阅读。传统的格言这样说,你应该从头开始并努力工作到最后。忘掉这句话吧。你完全可以拿起这本书,打开你感兴趣的章节,并从那里读起。每个章节都是独立的,其中有很多有用的交叉引用,可以使你了解各章之间是如何结合在一起的。当然,如果你喜欢遵循传统,从头读起也不失是一个好的选择。
各个章节都以相似的结构编写,你不会发现任何异样。每一章都可以分为下面几个部分:
本章主题
在每章的一开头,我会列出该章的要点。你可以从这里了解内容梗概。浏览一下这些主题,你就会知道将涉及哪些方面的内容。
章节主体
这里都是会让你感到本书物有所值的引人入胜的内容。
在章节主体中会时而出现一些“关键理念”。这些关键理念强调了重要的技巧、问题和态度,所以对这些内容要格外关注。其格式是这样的:
关键概念 重要内容。注意!
总结
在每章的结尾,这一小节将总结该章所讨论的主题。它为整章内容提供了一个概括性的视图。如果你真的时间有限,也可以只阅读每章的关键理念和这些总结部分。千万别告诉别人这是我说的。
然后,我将对比优秀程序员和糟糕程序员的行为方式,来总结你应该力求采取的重要态度。如果你有勇气,也可以根据这些例子来评价一下自己,但愿事实不会让你太伤心!
另请参见
这个列表会将你带入相关的章节,并解释它们是如何与当前的主题相关的。
思考
最后,我将列出一些需要思考的问题。这些问题并不游离于全书之外——它们是各章不可缺少的一部分。它们并不是要你回顾刚才所阅读的主题,相反,这些问题的目的是使你进行思考,并联想到该章之外的内容。这些问题分为两组:
— 深入思考 这些问题将深入地研究各章的主题,并提出一些重要的论点。
— 了解自己 这些问题将探查你及软件开发团队的工作习惯和编码成熟度。
不要略过这些问题!即使你很懒惰,以至于不想坐下来认真思考每个问题的答案(相信我,思考这些问题的答案会使你受益无穷的),至少也应该读一读这些问题并顺便做些思考。
本书的最后一部分包含了对这些问题的答案和讨论。这并不是一套直接的答案集——这些问题中很少可以直接用“是”或“否”来回答。在你思考并得到答案之后,不妨与我的答案做个比较。我的许多“答案”都包含了一些各章中未曾涉及的额外信息。
章节——详细说明
本书的每一章都将讨论一个单独的主题,涉及现代软件开发的一个具体问题领域。这些问题是人们编写糟糕的代码或糟糕地编写代码的常见原因。各章所描述的正确方法和态度,将使站在第一线的你生命力更旺盛。
本书共分为六篇;每一篇的目录页列出了各篇所包含的章节,并简要描述了每个章节所包含的内容。这些篇将由内而外地展开讨论。我们将从应当编写“何种”代码开始入手,最终着眼于我们应当“如何”编写这种代码。
我们的讨论从代码表面开始,主要是从宏观的角度分析如何编写源代码。我特意将这部分放在全书的最前面,因为剪裁代码是程序员们所真正关心的。
第1篇:代码表面
本篇描述了开发源代码的具体细节。我们将研究防御性编程的技巧,以及如何编排代码的格式和版式。然后,我们将继续探讨如何为代码命名和做记录。编写注释和处理错误的技巧也包含在本篇中。
第2篇:代码的神秘生命
接下来,我们将讨论编写代码的过程,即如何创建代码和处理代码。我们将了解构造代码的工具、测试方法、调试技术、编译可执行程序的正确过程以及优化等内容。最后,我们将思考如何写出安全的程序。
第3篇:代码的形成过程
然后,我们将从更广泛的角度讨论构造源代码的问题。我们将讨论代码设计方案的开发、软件的体系结构以及源代码是如何随时间而成长(或老化)的等问题。
下一步,我们将进入“宏观”层面,抬起头看看身边发生的事——软件工厂中的生活。如果不是一个开发小组的成员,我们是不可能写出大规模软件的,接下来的三篇包含了如何在团队中获益的技巧和方法。
第4篇:“一群”程序员
程序员不可能生活在真空中。(这需要有特殊的呼吸装备。)在本篇中我们将进入更广阔的世界,看一看好的开发习惯,以及如何将这些习惯融合到职业程序员的日常工作中。本篇还包含了良好的个人和团队编程技巧,以及版本维护系统的使用方法。
第5篇:开发过程的组成部分
在本篇中,我们将描述软件开发过程的一些规则:书写规范、执行代码审查以及时间预算的魔法。
第6篇:从高处鸟瞰
最后一篇将从高处向下审视开发过程,研究软件开发方法论,以及不同的编程规则。
如何使用本书
从头读到尾,或从你感兴趣的地方读起,都没问题。
重要的是,你要带着一种开阔的思维去读《编程匠艺——编写卓越的代码》这本书,并思考如何将你所读到的内容应用到你所做的事情上。聪明的人从自己的错误中学习,而更聪明的人则从别人的错误中学习。从别人的经验中学习总是有益的,所以在读过这本书之后,就可以询问你所尊敬的程序员的看法。浏览各章之后的问题并与这些程序员进行讨论。
希望你在学习编码匠艺的时候,能够得到快乐。读过之后,回顾一下,看看有多少匠艺是你愿意接受的,你的匠艺提高了多少,以及你的态度是否有所改进。如果什么也没有发生,那么这本书就失败了。我相信结果不会是这样的。
给导师的一点建议
这是一本指导经验较少的程序员的很好的工具书。对于这一点,我在撰写本书的过程中特别进行了考虑,并且本书已证明有助于提高程序员的成熟度和洞察力。
读本书的最佳方式,并不是用某种方法一口气学完所有内容。相反,最佳方式应该是分别阅读各章,然后与接受你指导的新手讨论该章的内容。各章最后列出的问题可以作为讨论的起点,所以从这些问题开始讨论,不失为一个好办法。
目 录
第1篇 代码表面第一部分
第1章 善于防守:健壮代码的防御性编程技巧 3
1.1 向优秀的代码前进 3
1.2 设想:最坏的选择 4
1.3 什么是防御性编程? 6
1.4 又大又坏的世界 8
1.5 防御性编程技巧 9
1.5.1 使用好的编码风格和合理的设计 9
1.5.2 不要仓促地编写代码 9
1.5.3 不要相信任何人 10
1.5.4 编码的目标是清晰,而不是简洁 11
1.5.5 不要让任何人做他们不该做的修补工作 11
1.5.6 编译时打开所有警告开关 12
1.5.7 使用静态分析工具 13
1.5.8 使用安全的数据结构 13
1.5.9 检查所有的返回值 14
1.5.10 审慎地处理内存(和其他宝贵的资源) 14
1.5.11 在声明位置初始化所有变量 15
1.5.12 尽可能推迟一些声明变量 15
1.5.13 使用标准语言工具 15
1.5.14 使用好的诊断信息日志工具 16
1.5.15 审慎地进行强制转换 16
1.5.16 细则 16
1.6 约束 17
1.6.1 约束的内容 19
1.6.2 移除约束 19
1.7 总结 22
1.8 另请参见 22
1.9 思考 24
1.9.1 深入思考 24
1.9.2 结合自己 24
第2章 精心布局:源代码的版面和样式 26
2.1 什么是关键 27
2.2 了解你的读者 27
2.3 什么是好的样式 29
2.4 使用括号 30
2.4.1 K&R括号风格 30
2.4.2 悬挂式的括号风格 31
2.4.3 缩进的括号风格 32
2.4.4 其他的括号风格 33
2.5 主宰一切的风格 33
2.6 内部风格(以及在哪里使用它们) 35
2.7 设立标准 37
2.8 正义的战争 39
2.9 总结 40
2.10 另请参见 42
2.11 思考 42
2.11.1 深入思考 42
2.11.2 结合自己 43
第3章 名正言顺:为有意义的事物起有意义的名称 45
3.1 为什么我们应该恰当地命名呢 47
3.2 我们对什么进行命名 47
3.3 名字游戏 48
3.3.1 描述性 48
3.3.2 技术上正确 48
3.3.3 符合语言习惯 49
3.3.4 恰当 49
3.4 具体细节 50
3.4.1 命名变量 51
3.4.2 命名函数 52
3.4.3 命名类型 53
3.4.4 命名名字空间 54
3.4.5 命名宏 55
3.4.6 命名文件 56
3.5 玫瑰不叫玫瑰 57
3.5.1 保持前后一致 58
3.5.2 利用上下文 58
3.5.3 使用对你有利的名称 59
3.6 总结 59
3.7 另请参见 61
3.8 思考 62
3.8.1 深入思考 62
3.8.2 结合自己 63
第4章 不言自明:编写“自文档化”代码的技巧 64
4.1 自文档化的代码 66
4.2 编写自文档化代码的技术 69
4.2.1 使用好的样式编写简单的代码 69
4.2.2 选择有意义的名称 70
4.2.3 分解为原子函数 70
4.2.4 选择描述性的类型 71
4.2.5 命名常量 71
4.2.6 强调重要的代码 72
4.2.7 分组相关信息 72
4.2.8 提供文件头 72
4.2.9 恰当地处理错误 73
4.2.10 编写有意义的注释 73
4.3 实用的自文档化方法 74
4.3.1 文学性编程 74
4.3.2 文档化工具 76
4.4 总结 78
4.5 另请参见 79
4.6 思考 79
4.6.1 深入思考 79
4.6.2 结合自己 81
第5章 随篇注释:如何编写代码注释 82
5.1 什么是代码注释 83
5.2 注释看上去是什么样的 84
5.3 多少注释是恰当的 84
5.4 注释中应该有些什么 85
5.4.1 解释为什么,而不是怎么样 85
5.4.2 不要描述代码 86
5.4.3 不要取代代码 86
5.4.4 确保注释有用 86
5.4.5 避免分心 88
5.5 实践 88
5.6 从审美的角度看注释 89
5.6.1 一致性 89
5.6.2 清晰的块注释 90
5.6.3 缩进的注释 90
5.6.4 行尾注释 91
5.6.5 帮助你阅读代码 91
5.6.6 选择一种维护成本较低的风格 92
5.6.7 分隔板 92
5.6.8 标志 92
5.6.9 文件头注释 93
5.7 使用注释 94
5.7.1 帮助你编写例行程序 94
5.7.2 错误修正通告 95
5.7.3 注释过时 95
5.7.4 维护和空洞无物的注释 96
5.8 总结 97
5.9 另请参见 98
5.10 思考 98
5.10.1 深入思考 98
5.10.2 结合自己 99
第6章 人非圣贤:处理不可避免的情况——代码中
的错误情形 100
6.1 从何而来 101
6.2 错误报告机制 102
6.2.1 不报告 103
6.2.2 返回值 103
6.2.3 错误状态变量 104
6.2.4 异常 104
6.2.5 信号 106
6.3 检测错误 107
6.4 处理错误 108
6.4.1 何时处理错误 109
6.4.2 可能的反应 110
6.4.3 代码示例 112
6.5 使地狱浮现 116
6.6 管理错误 118
6.7 总结 119
6.8 另请参见 119
6.9 思考 120
6.9.1 深入思考 120
6.9.2 结合自己 121
第2篇 代码的神秘生命第一部分
第7章 欲善其事,先利其器:使用工具构建软件 125
7.1 什么是软件工具 126
7.2 为什么要在意工具 128
7.3 使工具发挥作用 129
7.3.1 了解它能做些什么 130
7.3.2 学习如何驾驭它 130
7.3.3 了解它适合什么任务 131
7.3.4 检查它是否可用 131
7.3.5 找到了解更多信息的途径 131
7.3.6 查明新版本何时出现 132
7.4 哪个工具 132
7.4.1 源代码编辑工具 133
7.4.2 代码构建工具 136
7.4.3 调试和调查工具 138
7.4.4 语言支持工具 140
7.4.5 其他工具 141
7.5 总结 142
7.6 另请参见 143
7.7 思考 144
7.7.1 深入思考 144
7.7.2 结合自己 145
第8章 测试时代:测试代码的魔术 146
8.1 反思现实 148
8.2 谁、是什么、何时以及为什么 149
8.2.1 我们为什么要测试 149
8.2.2 谁来进行测试 150
8.2.3 测试的内容有些什么 150
8.2.4 何时进行测试 151
8.3 测试并不难…… 152
8.4 测试的类型 156
8.5 选择单元测试用例 160
8.6 为测试而设计 163
8.7 看!不要用手 164
8.8 面对故障该怎么办 165
8.9 你能管理它吗 166
8.9.1 缺陷跟踪系统 166
8.9.2 bug审查 168
8.10 总结 169
8.11 另请参见 169
8.12 思考 170
8.12.1 深入思考 170
8.12.2 结合自己 171
第9章 寻找缺陷(调试):当事情进展得不顺利
时该怎么办 172
9.1 生活的真相 173
9.2 bug的种类 174
9.2.1 从远处看 174
9.2.2 从近处看 175
9.2.3 从更近处看 178
9.3 消灭害虫 180
9.3.1 地下之路 181
9.3.2 地上之路 181
9.4 搜寻bug 182
9.4.1 编译时错误 182
9.4.2 运行时错误 184
9.5 如何修正缺陷 188
9.6 预防 190
9.7 除蜂剂、驱虫剂、捕蝇纸 190
9.7.1 调试器 190
9.7.2 内存访问校验器 191
9.7.3 系统调用跟踪 191
9.7.4 内核转储 191
9.7.5 日志 191
9.8 总结 192
9.9 另请参见 193
9.10 思考 194
9.10.1 深入思考 194
9.10.2 结合自己 194
第10章 代码构建:将源代码转换为可执行代码的过程 196
10.1 语言障碍 197
10.1.1 解释型语言 198
10.1.2 编译型语言 199
10.1.3 字节编译型语言 200
10.2 小题大做 201
10.3 构建软件版本 203
10.4 怎样才算是一个优秀的构建系统 206
10.4.1 简洁 206
10.4.2 一致 207
10.4.3 可重复和可靠 207
10.4.4 原子性 208
10.4.5 能够应付错误 209
10.5 技术细节 210
10.5.1 目标的选择 210
10.5.2 内务处理 212
10.5.3 依赖关系 212
10.5.4 自动构建 213
10.5.5 构建配置 214
10.5.6 递归地使用make 215
10.6 请发布我吧 215
10.7 构建大师是全能的吗 218
10.8 总结 218
10.9 另请参见 219
10.10 思考 219
10.10.1 深入思考 220
10.10.2 结合自己 220
第11章 追求速度:优化程序和编写高效的代码 222
11.1 优化是什么 223
11.2 是什么使代码不尽如人意 224
11.3 为什么不进行优化呢 225
11.4 为什么要进行优化 228
11.5 优化的具体细节 229
11.5.1 证明你需要进行优化 230
11.5.2 找出运行得最慢的代码 230
11.5.3 测试代码 232
11.5.4 优化代码 233
11.5.5 优化之后 233
11.6 优化的技术 233
11.6.1 设计更改 234
11.6.2 代码更改 237
11.7 编写高效的代码 241
11.8 总结 243
11.9 另请参见 244
11.10 思考 244
11.10.1 深入思考 244
11.10.2 结合自己 245
第12章 不安全感综合征:编写安全的程序 247
12.1 危险 248
12.2 敌人 250
12.3 借口,都是借口 252
12.4 感到很脆弱 253
12.4.1 不安全的设计和体系结构 253
12.4.2 缓冲溢出 254
12.4.3 嵌入的查询字符串 255
12.4.4 竞争状况 255
12.4.5 整数溢出 256
12.5 防范措施 257
12.5.1 系统安装技术 258
12.5.2 软件设计技术 258
12.5.3 代码实现技术 260
12.5.4 规程技术 261
12.6 总结 261
12.7 另请参见 262
12.8 思考 263
12.8.1 深入思考 263
12.8.2 结合自己 263
第3篇 代码的形成过程第一部分
第13章 崇尚设计:如何创作出优秀的软件设计 267
13.1 边设计边编程 268
13.2 我们要设计什么 269
13.3 为什么这么忙乱 270
13.4 良好的软件设计 271
13.4.1 简洁 272
13.4.2 优雅 273
13.4.3 模块化 274
13.4.4 良好的接口 275
13.4.5 可扩展性 278
13.4.6 避免重复 278
13.4.7 可移植性 279
13.4.8 符合语言习惯 280
13.4.9 良好地文档化 280
13.5 如何设计代码 280
13.5.1 设计方法和过程 281
13.5.2 设计工具 282
13.6 总结 285
13.7 另请参见 285
13.8 思考 286
13.8.1 深入思考 286
13.8.2 结合自己 287
第14章 软件体系结构:奠定软件设计的基础 288
14.1 什么是软件体系结构 289
14.1.1 软件蓝图 289
14.1.2 视图 290
14.1.3 在何时和何处进行体系结构设计 292
14.1.4 用体系结构来做什么 293
14.1.5 关于组件和连接 294
14.2 什么是良好的体系结构 295
14.3 体系结构风格 297
14.3.1 没有体系结构 297
14.3.2 分层的体系结构 298
14.3.3 管道和过滤器体系结构 299
14.3.4 客户端/服务器体系结构 299
14.3.5 基于组件的体系结构 302
14.3.6 框架 303
14.4 总结 303
14.5 另请参见 304
14.6 思考 305
14.6.1 深入思考 305
14.6.2 结合自己 305
第15章 改良与革命:代码是如何成长的 307
15.1 软件腐烂 308
15.2 警告信号 310
15.3 代码是如何成长的 312
15.4 相信不可能之事 315
15.5 对此我们可以做些什么? 316
15.5.1 编写新代码 316
15.5.2 维护现有代码 317
15.6 总结 319
15.7 另请参见 319
15.8 思考 320
15.8.1 深入思考 321
15.8.2 结合自己 321
第4篇 “一群”程序员第一部分
第16章 代码猴子:培养正确的编程态度和方法 325
16.1 各种各样的猴子 326
16.1.1 卖力工作的程序员 327
16.1.2 代码猴子 328
16.1.3 权威 329
16.1.4 半权威 330
16.1.5 傲慢的天才 331
16.1.6 牛仔 333
16.1.7 规划者 334
16.1.8 老前辈 335
16.1.9 狂热者 336
16.1.10 单线条程序员 337
16.1.11 拖沓者 338
16.1.12 勉强的团队领导 339
16.1.13 你 340
16.2 理想的程序员 340
16.3 那么该怎么办 341
16.4 最愚蠢的人 342
16.5 总结 343
16.6 另请参见 343
16.7 行为表格 344
16.8 思考 345
16.8.1 深入思考 345
16.8.2 结合自己 345
第17章 团结就是力量:团队合作与个人程序员 347
17.1 我们的团队——概览 348
17.2 团队组织 350
17.2.1 管理方法 350
17.2.2 责任划分 350
17.2.3 组织和代码结构 352
17.3 团队合作工具 352
17.4 团队疾病 354
17.4.1 巴别塔 354
17.4.2 独裁制 356
17.4.3 民主制 357
17.4.4 卫星站 359
17.4.5 大峡谷 361
17.4.6 流沙 363
17.4.7 旅鼠 365
17.5 良好团队合作的个人技巧和特点 366
17.5.1 沟通 366
17.5.2 谦虚 367
17.5.3 处理冲突 367
17.5.4 学习和适应能力 369
17.5.5 了解你的不足之处 369
17.6 团队合作原则 370
17.6.1 集体代码所有制 370
17.6.2 尊重别人的代码 371
17.6.3 编码准则 371
17.6.4 定义成功 371
17.6.5 定义责任 372
17.6.6 避免倦怠 372
17.7 团队的生命周期 372
17.7.1 团队的创建 373
17.7.2 团队的成长 375
17.7.3 团队合作 377
17.7.4 团队结束 377
17.8 总结 380
17.9 另请参见 381
17.10 行为表格 382
17.11 思考 383
17.11.1 深入思考 383
17.11.2 结合自己 383
第18章 安全措施:源代码控制与自我控制 385
18.1 我们的责任 386
18.2 源代码控制 387
18.2.1 修订控制 388
18.2.2 访问控制 390
18.2.3 处理代码库 390
18.2.4 在代码树上创建分支 391
18.2.5 源代码控制简史 393
18.3 配置管理 393
18.4 备份 395
18.5 发布源代码 396
18.6 应该将源代码放在哪里 397
18.7 总结 398
18.8 另请参见 399
18.9 思考 400
18.9.1 深入思考 400
18.9.2 结合自己 400
第5篇 开发过程的组成部分第一部分
第19章 注意细节:编写软件规范 403
19.1 规范到底是什么 404
19.2 规范的类型 405
19.2.1 需求规范 407
19.2.2 功能规范 409
19.2.3 系统体系结构规范 410
19.2.4 用户界面规范 410
19.2.5 设计规范 411
19.2.6 测试规范 412
19.3 规范应当包含哪些内容 413
19.4 规范编写过程 415
19.5 我们为什么会不编写规范 418
19.6 总结 420
19.7 另请参见 420
19.8 思考 421
19.8.1 深入思考 421
19.8.2 结合自己 421
第20章 代码审查:执行代码审查 423
20.1 什么是代码审查 424
20.2 何时进行审查 425
20.2.1 是否要进行审查 426
20.2.2 审查哪些代码 427
20.3 执行代码审查 427
20.3.1 代码审查会议 428
20.3.2 集成审查 431
20.4 审查你的态度 432
20.4.1 作者的态度 432
20.4.2 审查人员的态度 433
20.5 完美的代码 434
20.6 代码审查之外 435
20.7 总结 436
20.8 另请参见 436
20.9 清单 437
20.10 思考 438
20.10.1 深入思考 438
20.10.2 结合自己 438
第21章 时间估计:软件时间范围估计的魔术 439
21.1 在黑暗中摸索 440
21.2 为什么估计这么困难 441
21.3 压力之下 443
21.4 实用的估计方法 444
21.5 计划游戏 447
21.6 坚持 451
21.7 总结 454
21.8 另请参见 454
21.9 思考 455
21.9.1 深入思考 455
21.9.2 结合自己 455
第6篇 从高处鸟瞰第一部分
第22章 程序秘方:代码开发的方法和过程 459
22.1 编程风格 460
22.1.1 结构化编程 461
22.1.2 面向对象的程序设计 462
22.1.3 函数式编程 463
22.1.4 逻辑编程 464
22.2 烹饪方法:做什么与怎样做 464
22.3 开发过程 465
22.3.1 混乱 466
22.3.2 瀑布模型 468
22.2.3 SSADM和PRINCE 470
22.3.4 V模型 470
22.3.5 原型设计 471
22.3.6 迭代和增量开发 472
22.3.7 螺旋模型 473
22.3.8 敏捷的方法 474
22.3.9 其他开发过程 475
22.4 已经够了 476
22.5 选择一种过程 477
22.6 总结 478
22.7 另请参见 478
22.8 思考 479
22.8.1 深入思考 479
22.8.2 结合自己 479
第23章 编程领域大观:不同的编程分支 481
23.1 应用程序编程 482
23.1.1 塑装软件 483
23.1.2 定制应用程序 484
23.2 游戏编程 485
23.3 系统编程 486
23.4 嵌入式编程 488
23.5 分布式编程 490
23.6 网络应用程序编程 492
23.7 企业编程 494
23.8 数字编程 495
23.9 那又怎样 497
23.10 总结 497
23.11 另请参见 498
23.12 思考 498
23.12.1 深入思考 499
23.12.2 结合自己 499
第24章 下一步呢:结果好就一切都好 500
但下一步该做什么呢? 501
答案和讨论 504
参考书目 607