作为Mentor Graphics公司IC部门的一员,我有幸和一群聪明且有才华的软件工程师一起工作,共同开发了一些非常大型的系统。
早在1985年,Mentor Graphics公司就是最早使用C++开发实际大型项目的公司之一。那时,没有人知道该如何开发大型项目,也没有人预料到未有过使用经验的项目构建方法会出现这样的问题——成本超支、计划延迟、可执行文件庞大、性能低劣,以及构建时间昂贵得令人难以置信。
一路走来,我们收获了许多宝贵的经验教训——知识的获取是一个痛苦的过程。没有书籍可以帮助指导这种设计过程,也从未有人在如此大规模的系统上尝试使用面向对象的设计。
十年来,由于积累了大量有价值的经验,Mentor Graphics公司使用C++开发完成了数个大型软件系统,同时也为其他使用C++进行大项目开发的人开辟了一条道路,使他们不用再付出高昂的代价。
在十三年的C语言(后来转为C++语言)计算机辅助设计(Computer Aided Design,CAD)软件开发生涯中,我已经多次体会到:提前计划总会产生出更高质量、更易维护的软件产品。在Mentor Graphics公司,我一直强调要从项目的一开始就确保质量,要把确保质量作为设计过程中一个必不可少的组成部分。
1990年,我在哥伦比亚大学开始讲授研究生课程“面向对象设计与编程”。自1991年以来,作为这门课程的教师,我有机会将我们在Mentor Graphics公司从工业化软件开发过程中获得的许多经验与学生一起分享。来自数百个研究生和专业程序员的提问及反馈信息,帮助我明确了许多重要的概念。本书正是这些经验的总结。据我所知,这是第一本指导开发大型C++项目的书籍,也是第一本针对大型C++项目中出现的软件质量相关问题的书籍。我希望这些资料对于读者的工作非常有帮助,如同在我的工作中一样实用。
适用读者
本书主要是为有经验的C++软件开发人员、系统架构师和具有前瞻性的质量保证专业人员而写的。本书尤其适合那些致力于大规模软件开发(如数据库、操作系统、编译器和框架等)的人员阅读。
使用C++开发一个大规模软件系统,不仅要充分理解逻辑设计问题,与C++编程有关的大部分书籍中都包括了这些逻辑设计问题。若要进行有效的设计,还需要掌握物理设计概念,尽管这些物理设计概念与开发的技术紧密相关,但是物理设计概念的某些方面即便是专家级的软件开发人员也可能仅有很少的经验或者根本就没有经验。
当然,本书中提出的大多数建议也适用于小型项目。对于开发者来说,典型的做法是从一个小型项目开始,然后开始接触更具有挑战性的更大型项目。一个特定项目的范围经常会扩展,即使开始时是一个小型项目,后来也会变成一个大型项目。但是,在大型项目中忽略最佳实践策略所产生的直接后果比在一个较小型项目中要严重得多。
本书将高层设计概念与特定的C++编程规范结合起来,以满足下面两类需求:
(1)对面向对象设计,尤其侧重C++编程语言实际应用的书籍的需求;
(2)对描述如何使用C++编程语言开发非常大型系统的书籍的需求。
毫无疑问,这是一本进阶的书籍。本书既不适用于初学C++语法的读者,也不适合于用来学习未曾掌握的C++语法的读者。本书要教你如何充分有效地利用C++的全部功能去开发超大型系统。
总之,如果认为自己对C++已经掌握得很好了,但是想要了解更多的关于在大规模项目中如何有效地使用C++语言的内容,那么这本书是非常适合你的。
书中的例子
大部分人是通过例子来学习的。通常,我会提供更有实际意义的例子,避免采用只讲解设计的一方面,而在另外一些方面有明显错误的例子。我也尽力避免只讲述编程语言细节,而没有其他意义的例子。
除非特别指出,本书中的所有例子都表示“良好的设计。”因此,前几章介绍的例子与书中的所有推荐实践都是一致的。本书采用这种方式讲解的缺点是:大家在本书中所看到的示例代码可能与以往看到的代码有所不同,而此时可能还不能准确地知道为什么会不同。我认为,对本书提供的所有例子综合借鉴,可以弥补这个不足。
这些例子有两个地方值得注意:注释和包前缀(package prefix)。为了节省篇幅本书许多例子的注释直接省略了。所有出现的注释都是少而精的。不过遗憾的是,这里就是要求读者 “照我说的去做,而不是照我做的那样做”的地方——至少在本书中是这样的。让读者可以放心的是,在实践中,我在编写接口的时候添加了详细的注释,而不是在编写接口之后才添加注释。
第二个需要注意的是,本书前面几个例子中的包前缀使用不一致。在大型项目环境中包前缀是必需的,但是使用起来有些棘手,需要适应一段时间。在第7章正式提出来之前,我选择不使用注册的包前缀,以便能专注于介绍其他重要的基础内容。
当描述想要设计的目标功能时,为了使得文本更加简洁,在例子中使用了许多内联函数的文本注释。因为本书大部分内容与程序的组织问题直接有关,如内联,所以我倾向在例子中避免使用内联函数。如果一个函数被声明为inline,肯定有其正当的理由,而不只是为了表示方便。
使用C++开发大型系统是一系列工程方法的折衷。这里没有绝对的准则。使用像从不和总是之类的词汇进行描述是很吸引人的,但是这样的词语只能用来对内容进行简单的描述。在我的预计中,有能力阅读本书的C++程序员会认为这种笼统的描述有歧义——事实也确实如此。为了避免陷入这种局面,我会指出,什么(几乎)总是正确的情况,然后会给例外情况提供注释或者提示。
目前,有各种通用的文件扩展名,用于区分C++头文件和实现文件。例如:
头文件扩展名——.h、.hxx、.H、.h++、.hh、.hpp
实现文件扩展名——.c、.cxx、.C、.C++、.cc、.cpp
从始至终我们都使用.h扩展名标识C++头文件,使用.c扩展名标识C++实现文件。本书中,我们会频繁地称头文件为.h文件,实现文件为.c文件。最后需要说明的是,本书中的所有例子都已在SUN SPARC工作站CFRONT 3.0版本(SUN版本)上编译并校验过语法,同时在HP700系列机器上使用原生C++编译器进行过上述测试。当然,出现任何错误都只能由作者负责任。
阅读线路图
本书涵盖了很多内容。并不是所有的读者都拥有相同的知识背景。因此,我在第1章提供了一些(必备的)基础知识以帮助读者学习。专业的C++程序员可以选择略过这一部分或者根据需要简单参考一下。第2章包含了一些基本软件设计规则,希望每一位有经验的开发人员都可以很快掌握。
第0章概述大规模C++软件开发人员将会遇到的问题。
第一部分讲解基础知识。
第1章复习语言基础知识、通用设计模式和本书的风格约定。
第2章介绍C++项目开发中应该遵守的重要设计实践原则。
本书余下的内容分成两大部分。第二部分介绍了一系列大项目中与物理结构有关的重要论题。这些章(第3~7章)中的内容集中在编程方面,并且只精选适用于大型项目设计的内容,对许多读者来说这部分充满了全新的编程观点。这部分内容是按照“自底向上”的顺序介绍的,每一章都衔接了前一章的内容。
第二部分讲解物理设计概念。
第3章介绍系统的基本物理构件模块。
第4章阐述建立可测试、可维护、可重用的非循环物理依赖性层组件的重要性。
第5章讲解减少链接时依赖性的特定技术。
第6章介绍减少编译时依赖性的特定技术。
第7章讲解如何将前几章的技术应用到大型系统中。
最后一部分讲述逻辑设计和物理设计相结合的传统课题。这些章(第8~10章)讲述如何将一个组件作为一个整体进行设计,总结了大量与合理接口设计有关的问题,并解决了在大型项目环境中的实现问题。
第三部分讲解逻辑设计问题。
第8章综述组件总体设计需要考虑的重要因素。
第9章详细阐述创建一个组件的功能接口所涉及的问题。
第10章讲解在大型项目环境中实现对象的特定组织问题。
附录中的主题在本书中都有提到。
致谢
如果没有对Mentor Graphics公司建设和发展做出重要贡献的同事们的共同努力,这本书是不可能完成的。
首先,我很想感谢我的朋友、同事和大学同学Franklin Klein对本书所做的贡献。他审阅了本书的每一页原始手稿。Franklin对大多数软件开发人员都不熟悉的很多概念提出了很好的意见。Franklin的才华、智慧、学识、为人,以及领会有效交流微妙之处的能力在我遇见的人中前所未有。他详细评论并负责在内容、叙述顺序和表达风格等方面进行数次修改。
在排版期间,数位有天分和献身精神的软件专业人员审阅了本书的全部或大部分内容。他们愿意花费宝贵的时间审阅本书,对此我感到十分幸运。我要感谢Brad Appleton、Rick Cohen、Mindy Garber、Matt Greenwood、Amy Katriel、Tom O’Rourke、Ann Sera、Charles Thayer和Chris Van Wyk,他们花费大量的精力帮助我尽可能地提高本书的价值。我要特别感谢Rick Eesley,因为他提供了丰富的评论和实际的建议——我尤其要感谢他建议在每一章的结尾加一个小结。
许多专业软件开发人员和质量保证工程师审阅了部分章节。我要感谢Samir Agarwal、Jim Anderson、Dave Arnone、Robert Brazile、Tom Cargill、Joe Cicchiello、Brad Cox、Brian Dalio、Shawn Edwards、Gad Gruenstein、William Hopkins、Curt Horkey、Ajay Kamdar、Reid Madsen、Jason Ng、Pete Papamichael、Mahesh Ragavan、Vojislav Stokovic、Clovis Tondo、Glenn Wikle、Steve Unger以及John Vlissides,他们在技术上做出了贡献。我也要感谢Mentor Graphics公司的Lisa Cavaliere-Kaytes和Tom Matheson,他们为书中的一些图表提出了宝贵的建议。另外我还要感谢Eugene Lakos和Laura Mengel所做的贡献。
我要感谢以下读者,自从本书首次印刷以来,他们帮助我排除了很多不可避免的错误,对于这些错误我会承担所有责任:Jamal Khan、Dat Nguyen、Scott Meyers、David Schwartz、Markus Bannert、Sumit Kumar、David Thomas、Wayne Barlow、Brian Althaus、John Szakmeister以及Donovan Brown。
如果不是在哥伦比亚大学收到了一封推销信,里面有一份免费的Rob Murray著作的评论表,我也许永远不会出版这本书。因为我只在春季学期才授课,所以在我寄回所附表格的时候,要求将那本书寄到Mentor Grphics而不是哥伦比亚。之后不久,我接到了Pradeepa Siva(她是Addison-Wesley Corporate & Professional Publishing Group的)打来的电话,要确定我为何有这个不寻常的要求。让她相信了这个要求的合理性(也许有些自我吹嘘)之后,她说:“我想我的老板可能会愿意和您谈谈。”几天之后,我见到了她的老板——那个出版者。该社出版的“专业计算机丛书”品质卓越,我对此一直心怀崇敬。正是这种声誉让我最终答应为那套丛书撰写本书。
我非常感谢Addison-Wesley Corporate & Professional Publishing Group的成员。出版商Join Wait让我明白了理解他人和交流的重要性,这些都将是我受用终身的宝贵财富。从阅读和审阅大量书籍,到直接与每一个专业软件技术人员讨论,再到站在书店里四处观察潜在读者的购买习惯,John Wait一直在尽力把握行业的脉搏。
以Marty Rabinowitz为首的产品人员在各方面都致力于卓越。与其他出版商联系的学术型作者对我表现出了担心,但是Marty提出,在表达作者思想时应做到技术准确、易于使用,有美学魅力的提炼与加工,这让我十分高兴。我特别要感谢Frances Scanlon,为了排印本书,她努力付出,艰苦不懈。
本系列的技术编辑Brian Kermighan,在风格和内容上都做出了贡献,并发现了很多其他人没有发现的印刷错误和不一致的内容。他知识的广度和深度,与简洁的写作风格相结合,对本系列丛书的成功出版做出了非同寻常的贡献。
在本系列其他书中已证明的基本逻辑概念和设计实践,在本书中视为理所当然的内容,因此最后我还想感谢本系列丛书的其他作者。