UNIX程序设计艺术

来源:百度文库 编辑:神马文学网 时间:2024/04/29 08:20:42
1、前言
UNIX系统与其说是一操作系统,不如说是一口述史。-- Neal Stephenson
在学问和专门技术之间存在天壤之别。学问让你推演去做正确的事情;专门技术构成正确的事情的一种习惯性思维,几乎根本不需要有意识的去思考。
这本书里面有大量的学问,但是它主要是关于专门技术的。它将会设法教你UNIX专家所知道的关于UNIX开发的事情,而不是意识到他们所知道的。因此,比起大部分UNIX书,它是较少关于技术的,并且更多的是关于共享的文化-显示和隐式的文化,有意识和无意识的传统。它不是一本“怎样去做”的书,而是一本“为什么这样做”的书。
为什么这样做有重大的实际的重要性,因为太多的软件被拙劣的设计。它们中的大部分遭受了膨胀,维护起来是极度地困难,并且太困难而不能移植到新的平台上或以最初的程序员没有预料到的方式扩展。这些问题是糟糕的设计的征召。我们希望这本书的读者将会学到UNIX必须教授的关于好的设计方面的东西。
这本书被划分成四个部分:背景,设计,工具集,和社区。第一部分(背景)是哲学和历史,帮助为接下来所述各种的东西提供基础和激励。第二部分(设计)展开UNIX哲学的原则进入到关于设计和实现的更细节的建议。第三部分(工具集)集中在UNIX为帮助你解决问题所提供的软件。第四部分(社区)是关于那些使UNIX文化在它所涉及的方面如此高效的人与人的交往和协定。
因为这本书是关于共享文化的,我从未计划单独写它。你将会注意到这些文字包含了来自杰出的UNIX开发者,UNIX传统的塑造者的特约发表。在我邀请这些权威者来评论和讨论这些文字的期间,这本书经受了一个长期的公开的审核过程。在这本书的最终版本中,不仅覆盖了那个审核过程的结果,这些佳宾也被鼓励用他们自己的语态说话,引申和衍变,甚至和这些文字的主线不一致。
在这本书中,当我使用编辑上的“我们”的时候,这不是假装无所不知,而是反映了试图链接整个社区的专门技术的事实。
因为这本书的宗旨是传播文化,它比一般的技术书籍包含了更多的关于历史和民间传说和一些题外话。享受它吧;这些东西,也是作为一个UNIX程序员,你的教育的一部分。这些历史上的细节,没有哪一个单独上是至关重要的,但是它们全部的整体形态是重要的。我们认为用这种方式,它构造了一个更加有趣的故事。更重要的,明白UNIX从那里来的和是怎样走上这条路的,它会帮助你发展出对于UNIX风格的直觉的感觉。
出于同样的原因,我们拒绝写那些似乎结束的历史。你会发现异乎寻常地许许多多的对于我正在写这本书的时候的参考。我们不希望假装当前的实践反映了一些注定命运永恒的和完美的逻辑结果。写作时候的参考对于读者意味着警报,二年或三年或五年以后,这些相关的事实记录也许已经过时了,并且应该被仔细的检查。
这本书的其它方面既不是C语言的教程,也不是UNIX命令和API的指南。它不是为sed 或yacc 或Perl 或 Python的参考。它不是网络编程的初级读物,也不是对于神秘的X的详尽的指南。它也不是UNIX的内核和结构的漫游。其它书籍更好的覆盖了这些细节,这本书会适时给你指出它们。
超越了所有的这些技术细节,UNIX文化拥有一个发展超过数百万人年的娴熟的努力这种字面意义上的非书面的工程传统。这本书是带着理解那个传统,和加入它的设计模式到你的工具箱中这样的信仰写的,它将会帮助你成为一个更好的程序员和设计师。
文化由人构成,并且学习UNIX的传统方式是通过耳濡目染,从其它人那里和通过民间传说。这本书不是取代人与人之间的文化互渗,但是它通过允许你分接其他人的经验,能帮助加速这个过程。
注:【1】在1969和2003年之间的35年是一段很长的时间。在那期间,由于历史趋势的弯曲,大量的UNIX站点都逝去了,或许在某处,超过5千万的人年的工作量已经干劲十足地投入到了遍及全球的UNIX开发之中。
n       适合的读者:
如果你是一个经验丰富的UNIX程序员,经常处在要么指导初学编程的新手,要么和其他操作系统拥护者进行争论这样的场合。同时,你发现你很难把UNIX所带来的好处讲清楚。那么,这本书正在你所需要的。
如果你是一个在其它操作系统上有编程经验的C、C++或JAVA程序员,并且准备启动一个基于UNIX的项目。那么,你应该阅读此书。
如果你是一个UNIX操作系统下,具有初级到中级水平的用户。但是,只有比较少的开发经验。并且,想学习怎样在UNIX下有效的设计软件。那么,你应该阅读此书。
如果你是一个已经认识到UNIX传统也许有一些东西能让你从中受益的非UNIX程序员。那么,你应该阅读此书。我们确信你是对的,并且UNIX哲学同样适用其它操作系统。因此,比起其它的一般UNIX书籍,我们在这里更多关注非UNIX环境(特别是micorsoft操作系统)。并且,我们可以说,这里的工具和学习例子在一定程度上都是可移植的。
如果你是一个程序架构师,要为一个主流的通用市场或是纵向应用程序考虑平台的多样性或是各种不同的实现策略。那么,你应该阅读此书。它会让你明白UNIX作为一个开发平台的强大,和作为一种开发方式的开源的unix传统。
如果你正在寻找关于C语言编程细节或是怎样使用UNIX内核编程接口方面的书籍,那么这本书不是你需要的。关于这方面已经有很多优秀的书籍:在展示UNIX API的书籍中,《UNIX高级环境编程》是其中非常优秀的一部。《程序设计实践》也推荐所有的C程序员阅读(事实上是使用任何语言的程序员)。
n       怎样使用本书
这本书是理论实践相结合的。一部分是至理名言和通用的,另一部分是检验细节的UNIX下开发的例子学习。我们会在通用的原则和至理名言前面或是后面,用实例来描述它们:这些实例不是从玩具似的示范性程序中抽取出来的,而是每天都使用的真正的能工作的代码。
我们故意避免了让书里充斥大量的代码和文件清单似的例子,即使这样做可能会让书好写一些(有些地方可能会容易读一些)。很多编程书籍给出了太多的低层次的细节和例子,但是没有给读者对于说讲内容的一个更高层次上的认识。这这本书里,我们宁愿在反方向走向歧途。
因此,当你经常被应邀阅读代码和规范文件的时候,事实上只有少量相关内容被包含在书里。相反,我们会在web给出完整的例子。
有意思的是,这些例子能帮助巩固你所学到的原则变成近乎直觉的工作知识。理想情况下,你应该以近似于运行的UNIX系统上控制台的方式来阅读本书,手边在准备一个浏览器。任何UNIX系统都支持这样的方式,但是在LINUX系统上,为了对系统进行检查,这里用于例子学习的软件是预先安装的和立即可用的。该书中的各种指示物用来进行浏览和试验。这些指示器的介绍是按部就班的,因此,暂时的研究偏离主题不会打断那些必须连续的阐述。
注意:尽管我们已经尽一切力量去引用稳定可用的URL,但是我们办法来担保这一点。如果你发现引用连接已经失效了,使用你喜欢用的WEB搜索引擎,用经验常识和做短语搜索。这里我们建议你的方式和我们自己引用URL的方式差不多。
这本书里的大部分缩略语在第一次使用的时候都是以完整的形式出现。为了方便起见,我们在附录里提供了术语表。
参考资料通常按作者的名字。本应在书中正文出现的URL介绍或是我们怀疑的很容易失效的URL,还包括一些题外话、战争故事、玩笑,这些我们都用标号的脚注的方式来表示。
为了让这本书更容易被技术不是很强的人接受,我们邀请了一些非程序员人士进行阅读,并确定了那些有些模糊,但是对说明流程很重要的术语。我们还用脚注的方式给出了对于有经验程序员不需要的一些基本术语的定义。
注【2】:这些特别脚注是献给Terry Pratchett的,他的脚注的使用是相当。。。令人鼓舞的。
n       相关参考资料
以前,涉足这个领域的一些早期的UNIX开发者撰写了一些著名的论文和书籍。Kernighan 和 Pike的《unix编程环境》是其中比较优秀的,也是被认为很经典的。不过,现在看来,它有点过时了。它没有覆盖internet、WWW和象Perl, Tcl, and Python这样的解释语言的新潮流。
在我们写这本书的途中,我们学习了Mike Gancarz的《unix哲学》。这本书在阐述范围上十分优秀,但是依然没有尝试覆盖我们认为有必要阐述的所有领域。尽管这样,我们还是要感谢作者的暗示:非常简洁的UNIX设计模式是UNIX持久稳固和成功的非常重要的因素。
和本书相比,《程序员修炼之道》在有细微差别的各种不同的软件设计工艺(更多的是代码,较少关于高级别的问题划分)中,给出好的设计实践权衡方面,更像是一个幽默诙谐,并且明智的专题论文。作者的观点是UNIX下经验的产物,同时也是本书的有力补充。
在UNIX传统里,《程序设计实践》从一种更深入的角度覆盖了和《程序员修炼之道》部分相同的立场。
最后(并且被认为有煽动倾向),我们推荐的是《Zen Flesh  Zen Bones》,它们是佛教原始来源的重要的收藏品。对佛教的参考散落在整个书籍里面。我们引用它是因为,佛教提供了对于表达软件设计被证明是重要的思想的词汇,而采用其它方式却很难记住。带有宗教信仰的读者会认为佛教不是信仰,而只是治疗学的一种智力训练的形式,从纯粹的无神论角度来看,佛教确实是这样的。
n       使用本书的约定俗成
术语UNIX是开放组织技术上和法律上的商标,并且只有那些通过了开放组织精心制作的标准化测试而被鉴定的操作系统才能正式使用。在本书中,我们使用当前程序员群体中所认为的非严格的,更广泛意义上的UNIX含义,它是指或者是从共同祖先-贝尔实验室的原始UNIX代码继承下来,或者是模仿它的后代进行编写的任何操作系统(无论是不是正式的UNIX商标)。特别的,从这个意义上来讲,LINUX就是UNIX。
本书采用UNIX手册页的习惯,在工具名称后面用括起来的手册段进行标志,当我们想强调它是一个UNIX命令的时候,通常把它放到第一部分的介绍中。因此,例如,当读到munger(1)时候,munger作为一个程序,它的文档描述将会出现在UNIX手册页第一段中,当然如果你的系统提供它。第二段是系统调用,第三段是C库调用,段5是文件格式和协议,段8是系统管理工具。其它段随着UNIX系统的不同而不同,但是本书没有给出相关引用。进一步,你可以在SHELL提示符下敲入man 1 man 以得到更多相关内容(老的系统V版本UNIX,可能要求敲入man -s 1 man)。
有时候,我们提到一个UNIX程序的时候(例如,Emacs),没有手册段作为后缀,并且首字母大写。这是一个暗示,它的名字实践代表了本质上有相同功能的精心组织的UNIX程序族。并且,我们正在讨论的是它们的共性,例如,Emaces,它包含了xemacs。
在本书后面的不同知识点中,我们参照“守旧派”和“新学派”的方式。就像RAP音乐一样,新学派大约开始于1990年。在那个时代环境中,它和脚本语言的兴起,GUIs,开源UNIX和WEB联系在一起。守旧派指的是1990年以前(特别是1985年以前),昂贵的(共享的)计算机,专用的UNIX,shell脚本编程,C语言几乎统制了一切的那个世界。这里的差别是值得指出的,因为廉价的和没有内存限制的机器促成了UNIX编程风格上的重大改变。
n       我们的学习例子
为了证明一个观点,大部分编程方面的书籍都依赖于特殊构造起来的玩具似的例子。这本书不是这样的。我们的学习例子将是真实的,早就存在我们每天都使用的产品中的片断。下面是一些主要的产品:
cdrtools/xcdroast
这两个分开的工程经常在一起使用。cdrtools包是刻录CD-ROMs的一套CLI工具。以“cdrtools”为关键字在WEB上搜索一下。xcdroast程序是cdrtools的GUI前端。详见xcdroast project site.
fetchmail
程序fetchmail采用POP3 或IMAP邮件传输协议,从远程邮件服务器上获取邮件。详见fetchmail home page(或在WEB上用“fetchmail”进行搜索)。
GIMP
GIMP(GNU的图像处理程序)是一款具有涂、绘、图像处理全方位功能的图像处理软件,它采用独特的方式能够处理种类繁多的图形格式。在GIMP home page上能够获得源码。
mutt
在当前种类繁多的基于文本的UNIX电子邮件代理中,mutt邮件个人代理是其中最好的一款,它对MIME (Multipurpose Internet Mail Extensions)有非常好的支持,并且the use of privacy aids,例如PGP (Pretty Good Privacy) 和GPG (GNU Privacy Guard)。可以从Mutt project 站点上获得源代码和可执行程序。
xmlto
xmlto命令可以转换DocBook和其它XML文档为各种各样的输出格式,包括html,text和PostScript,可以在xmlto project site.上获得源码和文档。
为了降低读者理解这些例子所需要的代码数量,我们尽力选择了那些可以一例多用,可以阐述好几个不同的设计原则和惯例的学习例子。出于同样的原因,好多例子是从我的项目里挑选出来的。这里没有任何暗示说来自于我的项目的例子可能是最好的,我仅仅是十分熟悉它们,这对于例子的多用途说明是有好处的。
n       感谢
特约的贡献者(Ken Arnold, Steven M. Bellovin, Stuart Feldman, Jim Gettys, Steve Johnson, Brian Kernighan, David Korn, Mike Lesk, Doug McIlroy, Marshall Kirk McKusick, Keith Packard, Henry Spencer, and Ken Thompson)为这本书加入了大量的价值。特别是Doug McIlroy,在他的评论的透彻性和他贡献的深度上,远远超过了职责所求,显示出了和他30年前带给的为管理最初的UNIX科研小组的同样优秀的关注和奉献。
特别感谢Rob Landley和我的妻子Catherine Raymond,他们二个人对原稿的草稿作出了深入细致的逐行的审订。Rob深刻的和专心的评论事实上比在最终稿中的一整章内容更令人鼓舞,并且他做了大量的关于它呈现的组织和范围的工作。如果他写了全部促进我去改善的文字,我将不得不称他为合著者。Cathy是我的试验读者,代表了非技术用户。为了延伸这本书,让那些还不是程序员的读者所能理解,这些主要都是他做的。
在我花费写这本书的超过5年的时间里,它从许多其他人的讨论那里获益。Mark M. Miller帮助我完成了关于线程的启发。John Cowan提供了一些关于接口设计模式的见识,wily和VM/CMS的研究案的草稿,并且Jef Raskin给我展示了最小诧异原则是从那里来的。UIUC系统架构组在早期的章节上贡献了有益的反馈。UNIX做错了什么和深入的伸缩性这样的节是由于他们审阅的直接灵感。Russell J. Nelson 贡献了在第七章中链系的关于Bernstein的材料。Jay Maynard贡献了第三章中在MVS学习例子中的大部分材料。Les Hatton在语言一章中提供了许多有益的评论和由此启发的第四章中关于最佳的模块大小中的部分。David A. Wheeler贡献了很多观察入微的评论和一些例子研究材料,特别是在设计部分。俄国人Cox帮助改进了9张平面图的概观。Dennis Ritchie在关于C的一些历史点上纠正了我。
许许多多的UNIX程序员,比这里列出的多得多,在2003年的1月到6月之间的这本书的公开审核期间,贡献了建议和评论。一如既往,我发现在WEB上的开放的同行审查过程具有热情的挑战和热情的回报。也一如既往,对工作结果的任何错误的责任归我自己。
这本书的说明风格和涉及的一些内容己经被设计模式运动所影响。事实上,我动摇过给这本书起名叫UNIX设计模式的想法。我没有那么做,因为我不赞同这个运动中的一些隐式的中心法则,并且没有感觉到使用它所有的正式的注解的需要或承担它文化的包袱。然而,我的方法无疑的受到了Christopher的影响。
Alexander的工作(尤其是永恒的建筑方式和一种模式语言),我欠“四人帮”和他们学派的其它成员的大量的人情,因为他们给我展示了怎样可能的去使用Alexander的见识站在一个高层次上,而不仅是全然含糊的和无意义的概率,去讨论软件的设计。留意的读者应该明白设计模式:可复用面向对象软件的基础,是对设计模式的入门介绍。
当然,这本书的标题参考了Donald Knuth的计算机编程艺术。尽管和UNIX传统没有特定的关联,Knuth己经对我们所有人到造成了影响。
编辑具有的先见之明和想象力不是象他们看起来一样普通。Mark Taub就是这样的一个。他在一个停顿的项目上开凿出价值,并且巧妙的把我推入完成它。文案编辑对于散文风格很会赏析,并且有足够的能力改善那些不像他们自己的风格,甚至是很少有共同点作品,但是Mary Lou Nohr 就达到了那个水平。Jerry Votta 抓住了我对于封面的观点,并且使它看起来比我想象的更好。在Addison-Wesley出版社里的全体人员为使编辑的和生产的过程尽可能无痛苦达到了很高的水平,并且不仅仅在文字上愉快的接纳了我任性的控制倾向,而是深入到了这本书的视觉设计,艺术,和市场的这些细节上。
注【3】:Alexander的工作的正确评价,带有重要的部分的在线版本的链接,可以在《Some Notes on Christopher Alexander》上发现。
2、第一部分:背景
2.1、哲学
n       文化?什么的文化?
这是一本关于UNIX编程方面的书籍,但是在这本书里,我们将会大量使用文化、艺术、哲学这样的用词。如果你不是程序员,或者是和UNIX接触比较少的程序员,这些对于你来说,可能有点奇怪。但是,UNIX有一种文化;它有独具特色的编程艺术;并且伴随着它有一种很强大的设计哲学。理解这些传统会帮助你构建好的软件,即使你正在非UNIX平台上开发。
工程学和设计学的每一个分枝都有技术文化。在很多形形色色工程学中,对于从业者的教育,该领域的非成文的传统和正式的手册、教科书具有同等的重要性(并且,随着经验的增长,经常是比其更重要)。高级的工程师把这些只通过(如同佛教徒所做的)“特殊的传播,而非手稿”方式传递给他们后辈的隐性知识显彰在大量的有形体上。
软件工程是这些规则中一个普遍例外。技术变化的太快,软件环境日新月异,技术文化脆弱且短暂。当然,也不全是这样。较少数的软件技术被证明是有足够的生命力来发展出强大的技术文化、特色的艺术,和在工程师中代代相传的关联的设计哲学。
UNIX文化就是其中的一个。或者说,在21世纪的INTERNET文化是可被论证的另一个。自从80年代早期,随着这两者的成长,两者变得愈加难以分开,并且在本书中,我们也不会试着做这样的特殊努力。
n       UNIX的生命力
UNIX诞生于1969年,从那时到现在它以被流水线式的生产使用。整个时间跨越了计算机工业标准的好几个地质时期-比PC或工作站或微处理器,乃至视频显示终端,和具有同一时代的第一个半导体存储器的历史都要悠久。今天,在所有的分时系统中,只有IBM的VM/CMS可以声称能有较长时间存在下去,并且UNIX机器已经提供了几十万倍还多的运行时数。事实上,UNIX可能提供了比所有的分时系统加在一起所提供的计算还要多。
UNIX已经被认为可以在各种各样的机器上使用,比起其它任何操作系统所声称的都多。从超级计算机到手持和嵌入式网络设备,从工作站到服务器和过程控制系统和微机,UNIX大概预料到了的更多体系结构和更多零散的硬件,比任何3个其它操作系统加起来还要多。
UNIX已经支持压倒性的宽广使用领域。科研用工具、作为技术上的定制应用程序的友好主机,商业上畅销的商务软件的支撑平台,INTERNET的关键构成技术,没有任何其它系统能够同时在这些领域取得辉煌。
自从UNIX初期开始,每年都有这样自信的预言,UNIX会消亡或是被其它操作系统所排挤。然而UNIX,以它当今化身为Linux ,BSD ,Solaris ,MacOS X和半打其它形式的变体,在今天看起来,比以往任何时候都强大。
Robert Metcalf(Ethernet的发明者)说,如果有什么东西进步取代了Ethernet,那么它将是所谓的“Ethernet”,出于这个原因,Ethernet不会消亡。UNIX已经经历了好几次这样的转变
至少UNIX核心技术之一-C语言-已经广泛的移植到了其它地方。事实上,C作为系统编程中普遍使用的通用语言,很难想象在做软件工程过程中而不被用到。为了连接应用程序,UNIX也引入了现在以被广泛使用的带有目录节点的树型文件域名空间和为连接程序的管道。
UNIX的生命力和适应性简直是令人吃惊的。其它的技术象蜉蝣一样来来往往。机器在处理能力上增加了上千倍,语言在变异,工业惯例经历了数次的革命。然而,UNIX依然身处其中,依然在产出,依然在付帐,依然衷心的服务于这个行星上很多最优秀和聪明的的软件技术人员。
在计算,和相应软件开发速度方面的指数摩尔曲线的推理之一就是:每过18个月,一个人已有知识的一半就会过时。UNIX没有废止这种现象,而是在容纳它这方面做了很好的工作。UNIX有基本不变的根底-语言,系统调用,工具使用,事实上,这些能保持好多年不变,甚至是几十年。在其它方面很难预言什么将是稳定的。甚至整个操作系统周期性的废弃。在UNIX下,短暂的和持久性的知识之间有着清晰明显的区别,并且一个人提早知道的(有大约90%的确定性)各类知识,当它去学的时候,这些知识很可能到期了。UNIX命令是如此的忠诚。
UNIX的稳定性和成就的大部分毫无疑问是归功于其固有的强壮,归功于Ken Thompson, Dennis Ritchie, Brian Kernighan, Doug McIlroy, Rob Pike和其他一些早期UNIX开发者在开始时候就为将来做打算的设计决策。归功于一遍又一遍的被证明是合理的决定。但正是同样的归功于在早期围绕UNIX发展起来的设计哲学,编程艺术和技术文化。自从那时候起,这个和UNIX共生的传统就在不断的,成功的进行自我繁殖,复制。
注【4】:事实上,以太网已经被具有同样名字的不同技术取代了2次。一次是当同轴电缆被双绞线取代的时候,第二次是当千兆以太网流行起来的时候。
n       UNIX文化反面学术例子
对于那些已经喜欢上UNIX,和或许已经喜欢上它技术历史学家,UNIX的稳定性和它的技术文化无疑是最吸引人的地方。但是,作为多用途的分时系统的最初的UNIX程序,是为那些被个人工作站击败而迅速消失在历史烟河中的中型和大型计算机所开发的。于是,在被微软统制的主流商业桌面电脑市场领域中,有一定的怀疑余地认为它将不会不断的取得成功。
外行者经常打发UNIX当作学术上的玩具或是黑客玩耍的沙盒。一个被众所周知的争论,《UNIX痛恨者手册》,跟随的反对路线几乎和UNIX本身被记载的一样久,它的热爱者作为一群反常信仰的信徒和失败者而脱离出来。AT&T, Sun, Novell和其它一些商业厂商和标准化组织在给UNIX定位和市场推广方面都接二连三的犯下了很多大的错误,当然,这些都已经成为传说了。
UNIX看起来一直在摇摇欲坠濒于普及性好久了,以至于甚至来自于UNIX世界的内部都产生了它决不会实际上达到那里的怀疑。一些生性好疑的局外评论员的结论认为:也许UNIX太有用了而不会走向消亡,但是也会因为太难以使用而无法逃出密室;一个永久的小生境的操作系统。
LINUX和其它开源UNIX的兴起(例如现代各种BSD变体),在挫败怀疑者方面,比其它任何方面都要重要。UNIX的文化证明是太重要了,甚至10几年厂商的管理不善也不能将其扼杀。今天,UNIX社区本身已经控制了UNIX的技术和市场,并正在迅速地、显著地解决着UNIX的各种问题(在这方面,我们将在第20章更详细的检查)。
n       UNIX做错了什么
从1969年以来,就设计方面来说,认为UNIX中那一项设计是勿庸置疑的错误是非常困难的。到是也有一些普遍认为有问题的设计,但是它们中每一项在至今仍是倍受争议的,这些争议不仅仅是在UNIX爱好者之间,那些考虑和设计操作系统的广大社区的人们也加入其中。
UNIX上的文件在字节级别上没有进一步的结构。文件删除是不可以恢复的。UNIX安全模型被论证认为太原始。作业控制也很糟糕。对一些东西有太多的不同命名方式。持有的文件系统从肯本上就可能是一个错误的选择。我们将在第20章讨论这些技术问题。
但是,对于UNIX来说,大部分经久不衰的的争论多半是第一次被X windowing系统设计者显示提出来的哲学中最有特殊的一部分的推论。    X力争提供一种机制,而不是策略,支持图形操作系统所提供的非常通用的装置,并且推迟关于工具包和所见即所得界面的决定到应用程序级。UNIX的其它系统级别的服务显现出了类似的趋势。关于行为的最终的选择尽可能的向用户有利的方面推动。UNIX用户可以在多种SHELL中进行选择。UNIX程序通常提供许多行为选项和令人喜欢的精心制作的偏爱便利。
这种趋势反映了主要为技术用户所设计的操作系统的传统,并且随之而发生的信仰是:用户比操作系统设计者更了解他们自己真正需要的是什么。
这个原则是被贝尔实验室的Dick Hamming稳固的建立的,当在计算机即稀有又昂贵的50年代的时候他就坚持这一点。那个年代开放式计算站是必要的,在那里顾客们自己写程序,因为:用错误的方法解决正确的问题总比用正确的方法去解决错误的问题要好。
但是,这种非策略机制的方法的代价是,当用户能设置策略的时候,他必须设置策略。一些非技术性的终端用户发现UNIX丰富的选项和接口风格压倒性和倒退了系统,至少提供给了他们伪简单性。
从段时期来看,UNIX放入主义方式也许失去了一定数量的非技术用户。然而,从长期来看,它可能化劣势为优势,因为策略趋向于短生命线,而机制则相反。今天在界面所见即所得方面的风格太常见了,以至于堵死了将来的进化之路。(就像使用过时的X工具包的人所将要告诉你的感受的那样)。在竞争对手更多的捆绑策略或界面选择淡出视线以后,于是反面的反面是:“机制,而不是策略”也许会让UNIX重新获得新生。
注【5】:是的,Hamming 的“Hamming  距离”和“Hamming 码”
注【6】:Jim Gettys,X的架构师之一(也是这本书的贡献者),在《The Two-Edged Sword 》一文中,对于X的放任政策的风格向将来执行下去会有怎样的结果有很深的冥想。这篇短文,对于它的细节上的提议和对于its expression of the Unix mindset,都是非常值得读的。
n       UNIX做对了什么
最近LINUX爆炸式的增长,和INTERNET渐增的重要性,给了我们很好的理由支持,那些怀疑论者的例子是错误的。但是,即使怀疑论者的估计是正确的,UNIX文化也是值得学习的,UNIX和它周围的文化中的一些东西,比起其它任何竞争对手有明显的优势。
开源软件:
尽管在1998年以前,术语“open source”和开源的定义没有被发明,但是,自从UNIX诞生以来,具有同等意义的自由的共享源代码的开发就是UNIX文化的关键特性。
对于AT&T的原始UNIX的,和主要变种的伯力克UNIX头十年中,通常是带着源码发布的。这就使大量的其它好的事情尾随而至。
跨平台可移植性和开放标准:
跨越不同种类的计算机、厂商和特殊用途的硬件,能提供一致的,文档支持的应用程序编程接口(API),在这方面,UNIX仍然是唯一的操作系统。它是唯一的操作系统,能够支持从嵌入式芯片和手持设备,直到桌面机器,到服务器,对于特殊目的的大型数字计算设备和数据库后端更是自始至终的支持。
在现存的API中,UNIX的API是最接近写真正的可移植软件的硬件无关标准。不出意外的话,IEEE原来称作的Portable Operating System Standard,取了它们的首字母加上一个快速取得的后缀就形成了POSIX。对于这样一个标准,和UNIX具有同等价值的API是唯一的可信模型。
一些其它操作系统上,只有二进制的可执行程序和它们的出生环境一起消亡了,但是UNIX原始资料是永远存在的。一个给定的UNIX技术的文化至少能闪光和维持它们数十年,或是永远。
INTERNET和WWW:
美国国防部签订了TCP/IP协议栈第一份产品的合同,这有助于UNIX开发组织,因为那个时候UNIX大量的开放源码正在遭到置疑。除了TCP/IP,UNIX已经成为INTERNET服务提供商不可或缺的核心技术之一。自从80年代中期,家用操作系统TOPS退出历史舞台后,大部分的INTERNET服务器都依赖于UNIX。
微软可怕的营销策略已经动摇了UNIX在INTERNET世界中的霸主地位。然后,从TOPS-10进化而来的TCP/IP规范,在理论上是和UNIX背道而驰的,尝试使这样规范在其它操作系统上工作会遭受不兼容性、不稳定性和很多bug的困扰。这些理论和规范任何人都可以获得,但是工程习惯于仅在UNIX的世界中使它们稳固和真实运作。
在80年代早期,INTERNET技术上的文化和UNIX文化开始融合,并且现在已经是不可分割的共同体。INTERNET最现代化的外观,WWW的设计,应该象感谢它的祖先ARPANET一样感谢UNIX。特别的,对于WEB如此重要的统一资源定位的概念(URL),是UNIX各处统一的文件命名思想的范化。
开源组织:
最初围绕着早期UNIX开源发布而形成的组织从来没有消失-在90年代早期INTERNET爆炸性大增长以后,它新吸引入了家用机器上整个新一代热心的黑客。
今天,对于各种各样的软件开发来说,该组织都是强有力的支持团体。在UNIX世界里,有很多高质量的开源开发工具。开源UNIX程序通常等于,并且经常优于,他们自己拥有的具有同样意义的程序。带有完整工具包和基本应用程序套件的整个操作系统,可以在互联网上免费的获得。为什么抓来的代码,这时你能改编,重用,循环利用,并且节约你自己工作的90%呢?
代码共享的传统高度依赖于关于怎样协助开发和重用,这样难的的专业技术。并且不是依据抽象的理论,而是通过大量的工程实践-不明显的设计规则,允许功能程序不尽是作为孤立的一次性解决办法,而是作为一个工具包的增强部分。这本书的主要目的是阐明这些规则。
今天,萌芽开源运动正在带来新的活力,新的技术上的方法,和整个聪明年轻的一代程序员进入UNIX的传统。包括LINUX操作系统,和与之共生的象APACHE和Mozilla这样的开源工程,使得UNIX传统的主流可见性和成功达到了一个空前的程度。开源运动似乎处在赢得将来计算基础结构定义的标书的边缘,并且该基础结构的核心将是正在INTERNET上运行的UNIX机器。
自始至终的向下伸缩性:
大多数被吹捧为比UNIX更现代或更用户友好的操作系统通过锁定用户和开发者进入一个界面的策略,和宁愿狭隘和刚性也煞费苦心提供的应用程序编程接口,达到了它们繁华的外表。在这样的系统上,设计者有预期的任务非常简单-但是那些没有预期的任务通常是难以忍受的或是非常痛苦的。
另一方面,UNIX有深入的伸缩性。UNIX提供许多种方式把程序胶合在一起,这意味着它的基本工具包的组件能够合并产生单独工具包部分的设计者从未预期到的非常有用的价值。
UNIX的编程接口的多种风格的支持(经常被看做是弱点,因为它增加了对于终端用户来说系统感觉上的复杂度)也有利于伸缩性。没有想成为数据管道中简单一片的程序被强制承担在精心制作的GUI之上的复杂性。
UNIX传统把重点放在强调保持程序接口相对较小、干净和正交上-产生深入的可伸缩性的另一个特点。简单的事情是简单的和困难的事情至少尽可能简单,贯串这整个UNIX系统。
UNIX是黑客的娱乐:
忙于UNIX先机技术的具有教皇地位的那些人常常可能肯本不提它的非常有重要性的力量,这一类人放下了他们所有的浮名。UNIX是黑客的娱乐。
UNIX的拥护者看起来不时的就对黑客的贡献而没有答谢表示惭愧,尽管承认他们的娱乐可能不知何故的损害了他们的合法性。但是和UNIX打交道和为其开发是一种娱乐,是一个事实,并且,永远都会是这样。
没有多少任何人都永远用“乐趣”来形容的操作系统。事实上,在大部分其它环境下的开发被形象的比喻为摩擦和分娩。
to kicking a dead whale down the beach。经常听到的亲切的形容词是“可以忍受”或“不是非常痛苦”这样一类的。在UNIX的世界里,恰好相反,操作系统奖赏努力,而不是阻止它。在UNIX下经常编程的人达成共识,它不是作为一个用棍棒,使你通过全力的努力按照它的命令去做的敌手,而宁愿作为实践的积极的帮手。
这有真正的经济意义。在UNIX历史的早期,娱乐因素发起了一个道德圈。人们喜欢UNIX,因此为它编写更多的程序,使它更好用。今天,人们把构建完整的,产品级质量的开源UNIX系统当作是一个业余爱好。为了明白这是多么有价值,当你最近听说任何人科隆了OS/360,或是 VAX VMS 或是 Microsoft Windows仅仅是为了娱乐,请扪心自问。
或者,从设计的角度上来看,娱乐因素是微不足道的。作为程序员和开发者的类型的人,当他们不得不做出努力来做对于他们来说有挑战,但是刚好在他们能力之内的任务的时候,他们有“娱乐”。“娱乐”因此是高效率的象征。痛苦的开发环境浪费了努力和创造力。他们在时间,金钱,和机会上付出了巨大的隐式代价。
如果UNIX在所有其它方面是一个失败者,在开发中保持娱乐的方面,UNIX的工程文化将是值得研究的-因为娱乐是开发者高效、有效、多产的象征。
UNIX教训能应用在其它地方:
当倡导我们现在想当然的系统特征的时候,UNIX程序员已经积累了数十年的经验。即使非UNIX程序员也能从UNIX的经历学习中受益。因为UNIX使得实施好的设计原则和好的开发方式相对要容易,它是学习它们的最佳场所。
其它操作系统宁愿采用更困难的好的习惯做法,尽管虽然一些UNIX文化的教训是可以移植的。许多的UNIX代码能直接移植到任何支持ANSI C的操作系统上。
注【7】:其它操作系统一般有拷贝的或克隆的UNIX TCP/IP实现。It is their loss that they have not generally adopted the robust tradition of peer review that goes with it, exemplified by documents like RFC 1025 (TCP and IP Bake Off).
注【8】:这句话最初是由Stephen C对IBM MVS TSO 设备的宣传,或许作为YACC的作者更被广为人知。
n       UNIX哲学要素
UNIX哲学起源于Ken Thompson的关于怎样设计一个具有清晰的服务接口的小型的,但是有能力的操作系统的早期思考。关于在Thompson的设计范围外,如何获得最大的杠杆作用已经成长为UNIX文化学问上的东西。它全神贯注于沿着这条路上的许多来源的教训。
UNIX哲学不是流于形式上的设计方法。它不是从那些号称可以产生理论上完美的软件的高深莫测的计算机科学理论上抄袭下来的。它既不是在非常短的一个最终期限内,可以从动机不明的、糟糕的管理,和待遇很低的程序员那里魔法般的榨取出有创新的,但是可靠的软件的方法,也不是行政管理人员长期的幻想。
UNIX哲学是从底向上的,而不是至顶向下的。它是注重时效的和以经验为基础的。它不出现在官方的方法和标准里,而是在隐式的半自悟式的知识里,这是UNIX文化传播的专门技术。它鼓励用一定比例理性的怀疑论和理性的(经常是具有破坏性的)幽默来表达。
Doug McIlroy,UNIX管道的发明者和UNIX传统的奠基人之一,曾经说过:
1、是每一个程序只做好一件事情。如果要做新的工作,就构建新的程序,而不是通过增加新的特性而使老的程序变得复杂。
2、预期每一个程序的输出都是另一个的输入,即使是未知的程序。不要把输出和额外的信息混和在一起。避免严格的列形的或是2进制的输入格式。不用坚决要求交互式输入。
3、设计和构建软件,甚至是操作系统,应该是及早经过验证的,理想情况下是在几周之内完成这件事。毫不犹豫的抛弃笨拙的部分和重新构建它们。
4、使用不需要什么技能的偏爱的工具来减轻任务的编写,即使你不得不饶弯路来构建这些工具,并且预计到当你使用完它们后就会抛弃它们。
他最好这样总结:
编写只做一件事情,并且要做好的程序;编写可以在一起工作的程序,编写处理文本流的程序,因为这是通用的接口。这就是UNIX哲学。
Rob Pike,他是精通C语言的大师之一。在《Notes on C Programming》一书中提出了略微不同的观点:
1、你无法判定你的程序那里将会耗时。瓶颈出现在令人吃惊的地方,因此直到你已经证实瓶颈在那里的时候,不要试图做第二次猜测和陷入盲目的提升速度之中。
2、可度量的。直到你有一个标准之前,不要进行速度的调协,并且除非代码的一部分侵占了大部分资源,否则都不要这样做。
3、Fancy算法在N很小的时候速度较慢,并且N通常是小的。Fancy算法有很大的常数。直到你知道N经常将是较大的之前,不要使用Fancy算法。
4、Fancy算法比简单的算法容易犯错误,并且它们是比较难实现的。使用简单的算法和数据结构。
5、数据至上。如果你已经选择了正确的数据结构,并且组织的很好,那么算法将几乎经常是不言而喻的。数据结构,不是算法,是程序的核心。
6、没有规则6。
Ken Thompson,他设计并实现了最初的UNIX,用佛教创始人有价值的格言式的座右铭来加强了Pike的第四条规则:
当拿不准的时候,使用蛮力。
大部分隐式的UNIX哲学不是这些前辈所说的,而是他们所做的和UNIX自身建立的例子。从整体上看,我们能够抽象出下面这些观点:
1、模块性原则:写简单的,通过干净的接口可被连接的部件;
2、清楚原则:清楚要比小聪明好。
3、合并原则:设计能被其它程序连接的程序。
4、分离原则:从机制分离从策略,从实现分离出接口。
5、简单原则:设计要简单;只有当你需要的时候,增加复杂性;
6、节俭原则:只有当被证实是清晰,其它什么也不做的时候,才写大的程序
7、透明原则:为使检查和调试明显更容易而设计
8、健壮性原则:健壮性是透明和简单的追随者
9、表现原则:把知识整理成资料,于是程序逻辑能变得易理解和精力充沛的。
10、              最小意外原则:在接口设计中,总是做最小意外事情
11、              沉默原则:当一个程序令人吃惊什么也不说的时候,他应该就是什么也不说
12、              修补补救:当你必须失败的时候,尽可能快的吵闹地失败
13、              经济原则:程序员的时间是宝贵的;优先机器时间节约它。
14、              产生原则:避免手工堆砌;当你可能的时候,编写可以写程序的程序;
15、              优化原则:在雕琢之前先有原型;在你优化它之前,先让他可以运行;
16、              差异原则:怀疑所有声称的“唯一真理“
17、              可扩展原则:为将来做设计,因为它可能比你认为来的要快
如果你是UNIX新手,那么这些原则相当值得思考。软件工程课本里介绍了它们中的大部分。但是大部分操作系统缺乏正确的工具和传统来把它们转换成实践,因此大部分程序员不能带有任何的连贯性使用它们。他们已经接受了生硬的工具,糟糕的设计,过渡工作,和习以为常的代码膨胀-这些另UNIX的爱好者所惊讶的烦恼。
1、模块性原则:写简单的,通过干净的接口可被连接的部件;
正如Brian Kernighan曾经说过:“控制复杂性是计算机编程的本质”。调试比开发时间多,并且getting a working system out the door is usually less a result of brilliant design than it is of managing not to trip over your own feet too many times.
汇编,编译器,流程图,过程化编程,结构化编程,“人工智能”,第四代语言,面向对象,不计其数的曾经当作可以做为解决该问题而被吹捧和销售的软件开发方法学。所有的这些都已经失败了,如果要说成功的话,那么它们逐步增加了程序负责的一般级别到了几乎超出了人类所能理解的范围,这是唯一的成功。正如Fred Brooks的那句经典的话:没有银弹。
编写不流于形式的复杂程序的唯一方法是控制整体复杂度下降-通过定义明确的接口进行连接的多个简单的部件进行构建,这些大部分问题是局部化的,并且你有一些希望在不破坏整体的情况下而浓缩其中的一部分。
2、清楚原则:清楚要比小聪明好
因为维护是如此的重要和如此的昂贵,写程序好像最重要的就是交流,但这里的交流不是执行它们的计算机,而是将来阅读和维护程序的人(包括你自己)。
在UNIX传统里,超越这个建议的暗示就是给你的代码加上注释。好的UNIX实践也拥抱为将来的可维护性的算法和实现所做出的选择-不仅仅是复杂的代码容易孳生bug,也因为将来的维护者会很难理解这些复杂的代码。
另一方面,优美而清晰的代码,较少可能出现问题-并且更可能被下一个不得不修改它的人所立即理解。这是非常重要的,特别是当下一个人是比你晚出道好几年的。
决不要努力的去译解晦涩的代码3次。一次可能侥幸成功了-但是你发现你不得不第二次去理解它-因为第一次已经是很久以前的事情了,并且你已经忘记了它-那么是时候给代码加上注释了,以便第三次能相对来说少一点痛苦。
3、合并原则:设计能被其它程序连接的程序。
如果你的程序没有一个能互相交换的,那么编写成块的过于复杂的程序是很难避免的。
UNIX传统强烈建议编写那些编写那些读写简单的,文本的,面向流底,设备无关格式底程序。在经典的UNIX下,许多程序尽可能被编写做为一个简单的过滤器,它们以简单的文本流做为输入,并且以简单的文笔流作为输出。
尽管象神话一样流行,这个实践被喜爱,不是因为UNIX程序员痛恨图形用户接口。这是因为如果你不写接收和输出简单文本流的程序,那么把这些程序串接在一起是非常困难的。
文本流对于UNIX工具来说就象在面向对象框架中消息对于对象一样。文本流接口的简单性加强了工具集的封装。许多精心制作的进程间通讯的形式,例如远程过程调用,表现出了是程序彼此陷入太深的趋势。
为了是程序可以组合,是它们保持独立。文本流一端的程序应该尽可能的少的关于另一端的程序。它应该是很容易的实现替换一端而不用干预另一端。
GUI可以是非常好的东西。依据任何合理的想法,复杂的二进制格式有时候是不可避免的。但是在写GUI程序以前,思考一下你的程序的灵活的交换式部分是否能单独的分离出来,负荷工作的算法分离形成另一部分,然后用简单的命令流或是简单的协议把这两部分连接起来,这样的思考是明智的。在设计一个向周围传输数据的狡猾的二进制格式之前,是值得做试验的,看看你是否可以采用简单的文本的格式进行工作,并且采用通用目的的工具进行数据流的处理,是否可以接受一点解析上的负荷代价。
当一个序列化,协议式的接口不是为专为一个应用而写的时候,正确的UNIX设计是至少尽可能多的把应用程序的最基础的部分组织入定义明确的库中。这就打开了应用程序可以通过连接调用的可能性,或者多样的接口可以进行胶合而处理不同的任务。
4、分离原则:从机制分离从策略,从实现分离出接口。
在我们的UNIX做错了什么的讨论中,我们评说,X的设计者做了实现“机制,而不是策略”的基本决定-是X成为通用的图形引擎,把关于用户接口的决定留给了工具包和系统的其它级别。通过指出策略和机制在不同时间尺度上的变异趋势,和策略的改变比机制快的多,我们被证明是对的。所见即所得的GUI工具包的时尚可以来来往往,但是光栅的操作和合成是永恒的。
因此,把策略和机制硬性的合在一起有两方面的坏的影响:它使得策略是刚性的和很难改变响应用户的需求,并且它意味着如果试图改变策略,那么就会有强烈的趋势动摇机制。
另一方面,通过把它们两个分开,我们是可能在不破坏机制的前提下试验策略。我们也使得为机制编写测试非常容易。
在GUI上下文之外,这个设计原则有很广的应用。通常来说,它暗示我们应该寻找把接口从引擎分开的方法。
有效的分离方法之一是,例如,把你的程序写成供嵌入式脚本语言驱动的C服务例程库,用脚本语言而不是C语言来写程序的控制逻辑。这个模式的经典例子是Emacs编辑器,它使用一种嵌入的LISP解释程序去控制原本用C编写的编辑。我们在第11章讨论这种设计风格。
另一种方法是,把你的程序拆分成通过在SOCKET之上用专门的应用协议进行通讯的前端和后端程序协作处理。前端实现策略,后端是机制。这一对的整体复杂度要远远低于实现同样功能的整体上的单个程序,降低了出BUG的弱点和解约了生命周期的费用。
5、简单原则:设计要简单;只有当你需要的时候,增加复杂性;
许多压力程序趋向于更复杂(并且因此更昂贵和臭虫成灾)。一类这样的压力是技术上的大男子主义,那些聪明的程序员以他们能够处理复杂和迷惑的抽象的能力而引以为豪。他们经常和同类人比赛,看谁能够构建最难以理解和漂亮的复杂事物。和刚才的常常一样,他们的设计能力超出了他们实现和调试的能力,和昂贵的失败结果。
“难以理解的和优美的复杂事物”的观念几乎是一个矛盾的修饰手法。彼此为“简单和优美”而竞争的UNIX程序员是真的值得尊敬的-这一点是这些规则暗示,但是显示的提出来是非常有价值的。
甚至更多(至少在商业软件的世界里)的额外复杂性来源于那些基于一时的市场的项目要求,而不是客户想要的真正的东西或是能实践发布的软件。许多好的设计在市场的一堆“功能列表”下被扼杀-那些功能,经常是客户永远也不会用到的。随之是恶性循环的操作。竞争者认为它不得不加入更多的浮华的特性来与浮华的特性竞争。不久以后,大块膨胀的是工业标准,并且每一个人都在使用巨大的,甚至是那些开发人员都不能解决的臭虫成灾的程序。
不论那一种方式,最终每一个人都迷失了。
避免这些问题的唯一办法是鼓励那些以小为美而著称,积极抵制膨胀和复杂的软件文化:把高价值放到一个简单解决方案上的工程传统,它期待分裂程序系统为多个小的相互协助的片断的方法,并且它自发地努力同用大量的浮华特性粉饰的程序(或者,更坏的是,围绕浮华特性去设计的程序)进行斗争。
那将会是更像UNIX的文化。
6、节俭原则:只有当被证实是清晰,其它什么也不做的时候,才写大的程序
这里的“大”有代码体积庞大和内在复杂型两方面含义。允许程序变大有损于可维护性。因为人们很不情愿扔掉很多工作产生的可见的成果物,大的程序引起过度的投资和接近失败或不理想的。(我们将在第11章用更多的细节来检验软件正确的大小问题)
7、透明原则:为使检查和调试明显更容易而设计
因为调试经常在开发的1/3或是更多的时间,早期做的有利于调试的工作是一件非常好的投资。通常使调试轻松的有效方法是为透明和可发现性而设计。
当你看一个透明的软件系统的时候,你能理解明白它正在做什么和怎么做的。当程序有便利的手段监视和显示它的内部状态,因此你的程序不仅是功能良好的,而且功能良好是可见的,这就是可显示性。
贯串整个项目,为这些品质而设计都将会有暗示。最小程度上,它暗示了调试选项不应该是事后最小限度考虑。更合适的是,在一开始它们就应该被设计-从这个角度上看,程序即能证明自身的正确性,也能够向将来的开发者传达了,原来的开发者解决问题的思维模型。
对于一个要证明自身正确性的程序,需要使用十分简单的输入和输出格式,以便于容易检查在合法性输入和正确的输出之间的正确关系。
为透明性和可见性的面向对象设计,也鼓励那些容易被其它程序员维护的简单接口-特别是,测试和监控装置和调试脚本。
8、健壮性原则:健壮性是透明和简单的追随者
当在超出设计者的保证的非预期条件下,和正常情况下的执行效率都很好的时候,该软件被称为健壮的。
大部分软件是脆弱的和臭虫成灾的,因为大部分程序对于人脑来说太复杂而不能立刻理解全部。对于一个贪婪侵占系统资源的程序,当你不能给出正确的理由,你不能确信它是正确的,并且如果它出现了问题,你不能处理它。
跟随着获得健壮的程序的方法是,是使它们的内部对于人类的思考方式来说是简单的。有两个主要的方法可以做到这一点:透明和简单。
为了获得健壮性,在能容忍不寻常的或是极端的大容量输入方面的设计也是重要的。牢记合成原则的帮助。输入由其它程序来产生的软件对于压力测试来说,是声名狼藉的(例如,据说原来的UNIX编译器需要小的升级来很好的处理YACC的输出。)。这些棘手的情况对于人类来说,看起来是无用的。例如,接受空的连表/字符串/等等,甚至在人类简直不或从不提供空的字符串的地方,避免象产生机械的输入这样的情形的不得不需要进行的特殊案例。
在古怪的输入下,成为健壮的程序的一条重要的策略是,避免在你的代码里有特殊的案例。bug经常潜伏在为特殊案例处理的代码中,和在试图处理不同的特殊案例的部分中间的交互中。
当你能考虑它,并且能立即明白它所正在表达的,我们评说该软件是透明的。当所正在表达的对于人脑来说足够的不复杂,能够不用过度的劳累就能推论出所有潜在的案例,这就是简单的。你的程序越拥有这两种品质,它们将越是健壮的。
模块性是一个组织它们和是它们较为简单的方法。有其它的方法为简单而战。这里说得这个就是另外的一个。
9、 表现原则:把知识整理成资料,于是程序逻辑能变得易理解和健壮的。
即使最简单的程序逻辑对于人类来说也是难以验证的,但是相当复杂复杂的数据结构还算是易于理解和考虑的。为了明白这一点,比较一个有50个指针节点的图表和50行流程图的程序的表示和表现能力。或者,比较一个用转换表和等价的switch语句的数组初始化。在透明和清晰上的不同是戏剧性的。
数据比程序逻辑更容易驾驭。随后当你在哪里看到在数据结构的复杂性和代码的复杂性之间的选择的时候,选择前者。更多的:在进化一个设计的过程中,你应该积极的寻找把复杂性从代码转向数据的方法。
UNIX社区没有发起该见识,但是大量的UNIX代码显示了它的影响。特别是,在操作指针方面的C语言技巧,从内核向上的各个级别的代码都鼓励可被动态更新的引用结构的使用。Simple pointer chases in such structures frequently do duties that implementations in other languages would instead have to embody in more elaborate procedures.
10、              最小意外原则:在接口设计中,总是做最小意外事情
这个作为最小惊讶原则也是被广为人知的。
容易使用的程序是那些要求用户学习较少新知识的程序-或者,换言之,容易使用的程序是那些非常高效的把用户先前已有的知识连接起来的程序。。
因此,在界面设计中,要避免那些无理由的新鲜事物和过分的小聪明。如果你在写一个计算器程序,“+”应该总是意外着加法。当设计界面的时候,在界面的感观上模仿得相似或类似于你的用户可能熟悉的程序。
注意你的预期的听众。他们可能是用户,他们可能是其它的程序员,或者他们可能是系统管理员。在这些群体中什么是最小令人惊讶的是不同的。
最小吃惊原则的反面是避免把东西做的浅层次上的相似,但是实际上一点也不同。这是极端背信弃义的,因为外表相似引发了错误的预期。把事情明显的分开比把它们做的几乎一样要好。
11、              沉默原则:当一个程序令人吃惊什么也不说的时候,他应该就是什么也不说
UNIX上最老的和最持久稳固的设计原则之一就是,当一个程序没有任何感兴趣的或是令人吃惊的东西要说的时候,它应该闭嘴。行为端正的UNIX程序不冒失的做它们的工作,只需要最小限度的忙乱和干预。沉默是金。
“沉默是金”原则最初进化,是因为早期的UNIX视频显示。在1969年迟钝的显示终端上,每一行额外的输出都是在用户时间上的严重浪费。限制远去了,但是为精简的优秀理由保留了下来。
我认为UNIX程序的精简是风格的中心特性。当你程序的输出成为另一个的输入的时候,它应该是很容易的挑出需要的比特。同时对于人们来说,它是人类因素的必需品-重要的信息不应该和冗长的程序内部行为的信息混和在一起。如果所有的显示信息都是重要的,那么重要的信息是很容易找到的。
良好设计的程序对待用户的注意力和专心作为一种宝贵的和有限的资源,只有当必须的时候才被要求。
(我们将在第11章的结尾用更多的细节讨论沉默原则和对于它的理由)
12、              修补补救:当你必须失败的时候,尽可能快的吵闹地失败
软件在它失败的方式上应该是透明的,和正常的操作一样。软件通过自适应能应付非预期的条件是最好的,但是最坏类型的bug是,那些修复没有成功,和那些直到很长一段时间以后,否则不会显示出来的隐蔽的造成讹误的问题。
因此,写软件的时,要写那些可以尽可能优雅的应付不正确的输入和它自己的执行错误软件。但是,当无法达到这一点的时候,要以一种使问题的诊断尽可能简单的方式使它失败。
Postel的规定也认为:在你所接受方面是不拘泥的,并且在你所发送方面是保守的。“。当时,Postel所说的是网络服务程序,但是这个潜在的思想是非常通用的。设计良好的程序通过从病态形式的输入中做它们尽可能的识别,来和其它程序合作。它们要么吵闹的失败掉,要么传递严格干净和正确的数据给链中的下一个程序。
然而,也要注意这个警告:原来的HTML文档被推荐:在你所接收的方面是慷慨的,并且从那时候到现在它使我们很苦恼,因为每一个浏览器接收一个不同的规范的超集。规范应该使慷慨的,而不是它们的解释。
McIlroy认为我们应该为慷慨而设计,而不是修正带有许可实现的不充分的标准。否则的话,正如他所恰当的指出的那样,所有的这些太简单了而不能在陈词滥调的困境中结束。
13、              经济原则:程序员的时间是宝贵的;优先机器时间节约它。
在早期的UNIX小型机时代,这个仍然是相当激进的想法。现今,随着全部的制造业的发展和大部分用户在廉价的机器的周期上的冲浪,它看起来太明显而不比说了。
可是,不知何故,实践似乎完全没有跟上事实。如果我们把这个座右铭真正的认真的贯串在软件开发中,大部分程序应该采用象Perl, Tcl, Python, Java, Lisp,甚至shell这样的,把程序员从内存管理的负担中解脱出来的高级语言来编写。
事实上,在UNIX的世界里这正在发生,尽管在这范围之外,大部分应用程序厂商看起来似乎仍然采用老式的用C或是C++编码的UNIX策略。在本书的稍后我们会讨论该策略和详细的权衡。
节省程序员时间的另一个明显的方法是,教机器做更多的低级别的编程工作,这就导出。。。
14、              产生原则:避免手工堆砌;当你可能的时候,编写可以写程序的程序;
人类在力言细节上是声名狼藉的糟糕。因此,任何一种程序的手工处理都是延期和错误的丰富来源。你的程序说明书越是较简单的分离的,人类设计者越是可能使它正确。生成的代码几乎总是比人类手工编写的要便宜和可靠。
我们都知道这是事实,但是我们经常没有考虑它的含意。对于人类编写的那些重复性质的和令人厌烦的高级别语言代码just as productive a target for a code generator as machine code.。(High-level-language code that‘s repetitive and mind-numbing for humans to write is just as productive a target for a code generator as machine code)当它们能提高抽象级别,也就是说当生成器的规格语言比生成的代码简单的时候,并且生成的代码以后不用手工维护的时候,是值得使用代码生成的。
在UNIX传统里,对于易倾向于错误的细节工作,代码生成器有大量的使用。解析/词法 生成器是经典的例子。Makefile生成器和GUI接口构建器是较新的一些。
(我们将在第9章覆盖这些技术)
15、              优化原则:在雕琢之前先有原型;在你优化它之前,先让它可以运行;
对于原型第一,最基本的论点是Kernighan 和Plauger的:现在交付功能的90%比永远无法交付的100%功能要好。至于略微不同的理由,Donald Knuth普及“过早的优化是万恶之本”的观察。并且,他是对的。
在瓶颈被知道以前,急于去优化可能是唯一的比特性蔓延更毁坏设计的错误。从令人曲解的代码到不能理解的数据规划,以损害透明性和简单性为代价的关于速度或内存或磁盘方面令人迷糊的用法的结果到处都是。它们卵化出了无数的问题和耗费了数百万的人时-经常,在一些资源的使用上只是获得边缘上的收获比起调试时间来,有非常少的花费。
经常令人不安,过早的局部优化实践上会阻碍整体最佳化。过早的优化设计的一部分经常防碍变化,这将使得跨越整个设计有大量的更高的代价,因此你会以劣等的性能和及其复杂的代码而告终。
在UNIX世界里,有一个长期建立的和非常显示的传统,它说:原型,然后是优化。在你优化它之前,先让它运作。或者:首先让它工作,然后让它工作的快。极限编程领袖,Kent Beck,在一个不同的文化里操作,经常放大整个说法到:使它运行,然后使它正确,然后使它快速。
所有的这些引证的延伸都是一样的:使你的设计正确,在你尝试调谐之前,采用非优化的,慢的,内存密集的实现。接下来,系统的调谐,寻找那些在局部复杂性上最小可能增加而能获得最大的性能受益的地方。
对于系统设计来说,原型和优化通用重要-比阅读很长的规范,判断一个原型是否做了你所想要的,这是十分容易的。我记得在任何人谈论“快速原型”或“敏捷开发”之前,在Bellcore的一个开发经理对抗“需求”文化很多年了。他不愿意发出很长的规范;他宁愿系紧一些shell脚本和awk代码的组合,大概表达什么是需要的,告诉顾客派遣一些职员到他这里几天,于是有顾客过来,并且看他们的职员使用这个原型,并且告诉他是否他们喜欢它。如果他们喜欢,他会说:从现在开始的几个月里,安如此这般的成本价格,你能让他具有工业级的强壮。他的估计趋向于精确,but he lost out in the culture to managers who believed that requirements writers should be in control of everything.
使用原型去认识你不必实现的特性有助于性能的优化。你不必去优化你没有写的东西。现存的大多数强大的优化工具也许是杀手锏。
我最多产的时期之一是扔掉1000行代码-Ken Thompson
(我们将在第12章更深入一些的探究关于相关联的思想)
16、              差异原则:怀疑所有声称的“唯一真理“
即使最好的软件工具,由于它们的设计者的想象,也是趋向于有限的。没有人足够聪明到为每一件东西优化,或是预期他们的软件可能投放的所有的用户。刚性的设计,不愿意和余下的世界交谈的封闭的软件是自大的非健康形式。
因此,对于软件的设计和实现,UNIX传统包含了一种健康的猜疑“唯一真理”的方法。它拥抱多种语言,开发的可扩展的系统,和无处不在的定制钩子。
17、              可扩展原则:为将来做设计,因为它可能比你认为来的要快
如果相信其它人所声称的“唯一的真理”是不明智的,关于你自己的设计相信他们甚至是愚蠢的。永远不要假定你有了最终的答案。因此,为你的数据格式和代码留下增长的空间。否则,你会经常发现你锁入了不明智的早期选择,因为当维护向后的兼容性时候,你不能改变它们。
当你设计协议或文件格式的时候,为了可扩展,使它们充分的自描述。总是,总是或者包含一个版本号,或者从自包含,自描述子句合成格式,通过这样一种方式能够容易的添加新的句子和去除老的,而不会搞乱可读格式的代码。UNIX经验告诉我们在制作自描述的数据规划时的微小额外的向上一些,将会以不必破坏事情就能向前进化它们这样的能力来数千倍的回报给你。
当你写代码的时候,组织它以便将来的开发者能够插入新的功能到框架里,而不必仍弃和重建框架。这个规则不是加入你还不需要的特性的特许。建议写你的代码以便以后当你确实需要增加特性的时候是容易的。使结合处可伸缩的,并且在你的代码里放上“如果你曾经需要。。。”的注释,你把这种优雅归公于在你之后那些将会使用和维护你的代码的人。
在将来你也将会处在这种场合,在更多的最近的项目压力下维护那些你可能已经忘了一半的代码了。当你为将来设计的时候,你保留的明智可能是你自己的。
注【9】:
n       在一节课中的UNIX哲学
所有的哲学真正的浓缩为一个铁一样的定律,高明的工程师的神圣的“KISS 原则”无处不在。
UNIX给你一个应用KISS原则的优秀的基础,这本书的剩余部分将帮助你怎样学。
n       应用UNIX哲学
这些哲学原则不是只是含糊的一般性。在UNIX的世界里,它们直接来源于经验,并且导致了特殊的规定。其中的一些,在上面我们已经揭露了。这里决不是毫无遗漏的列表“:
任何事情都可以是源和目的无关的过滤器所做的那样。
如果完全可能,数据流应该是文本格式的(这样它们能够被查看和用标准工具过虑)
如果完全可能,数据库规划和应用程序协议应该是文本格式的(人类可读和人类可编辑的)。
复杂的前端(用户界面)应该从复杂的后端干净的分离出来。
只要有可能,在用C编码之前,采用解释语言搭键原型
编写每件事物用混和语言要比用单一语言好,如果并且仅仅如果一个人喜欢使程序过于复杂,那么就使用一种语言
在你所接受的方面是宽松的,在你所发出的方面是严格的。
当过虑时,决不要扔到你不需要的信息
小是美丽的。Write programs that do as little as is consistent with getting the job done
我们将会看到从它们继承而来的UNIX设计规则,和指示,在这本书的剩余部分一遍又一遍的应用。毫不令人吃惊,它们趋向于聚合在其它传统里来自于软件工程的非常好的实践。
n       加之看问题的态度
当你明白正确的事情的时候,去做它-在短期内,这看起来像是更多的工作,但是最后,它是最少努力的途径。如果你不懂什么事情是对的,做最小的必须让工作完成,至少直到你领会什么是正确的事情之前。
为了正确去做UNIX哲学,你必须忠诚其优点。你必须相信软件设计是一种工艺,和你所能集聚的所有的智丽,创造力和激情具有同样的价值。否则,你不会注意过去通往设计和实现简单的、老套的方式。当你本应该思考的时候,你将会仓促行动编码。当你本应该无情的简单化的时候,你粗心的使它变复杂-并且,接下来你会奇怪,为什么你的代码膨胀和调试是如此的困难。
为了正确去做UNIX哲学,你必须足够重视你自己的时间,决不要浪费它。如果有人已经解决一个问题一次,不要让自尊心或是政见把你吸入又一次解决这个问题中,而不是重用它。同时,不要比你不得不工作的更努力。用巧妙的工作代替,保存额外的努力为当你需要的时候。尽你可能的学习你的工具和自动化每一件事情。
软件设计和实现应该是一件高兴的艺术,一种高级别的游戏。如果这个看法看起来荒谬的或是含糊的,对于你来说是令人为难的,停下来并且思考一下。问你自己你已经忘记了什么。为什么你设计软件,而不是做其它的事情去赚钱或是打发时间?你一定曾经有过软件是值得你的激情的想法。
为了正确去做UNIX哲学,你需要有那种态度。你需要注意。你需要扮演。你需要心甘情愿的去探索。
我们希望你将会把这种态度带入这本书的剩余部分。或者,至少,这本书将会帮助你重新发现它。