Month: February 2010

Unix 哲学--<<Unix编程艺术>>摘要

Posted by – February 23, 2010

Unix 哲学--<<Unix编程艺术>>摘要

要 良好的运用Unix 哲学,你就应该不断追求卓越。你必须相信,软件设计是一门技艺,值得你付出所有的智慧、创造力和激情。否则,你的视线就不会超越那些简单、老套的设计和实 现;你就会在应该思考的时候急急忙忙跑去编程。你就会在该无情删繁就简的时候反而把问题复杂化——然后你还会反过来奇怪你的代码怎么会那么臃肿、那么难以 调试。

要良好地运用Unix 哲学,你应该珍惜你的时间决不浪费。一旦某人已经解决了某个问题,就直接拿来利用,不要让骄傲或偏见拽住你又去重做一遍。永远不要蛮干;要多用巧劲,省下力气到需要的时候再用,好钢用在刀刃上。善用工具,尽可能将一切都自动化。

软件设计和实现应该是一门充满快乐的艺术,一种高水平的游戏。如果这种态度对你来说听起来有些荒谬,或者令你隐约感到有些困窘,那么请停下来,想一想,问 问自己是不是已经把什么给遗忘了。如果只是为了赚钱或是打发时间,你为什么要搞软件设计而不是别的什么呢?你肯定曾经也认为软件设计值得你付出激情……

所有的Unix 哲学浓缩为一条铁律,那就是各地编程大师们奉为圭臬的“K.I.S.S”原则:

Keep It Simple, Stupid!

==================================================

也许Unix 最持久的异议恰恰来自Unix 哲学的一个特性,这一条特性是X window 设计者首先明确提出的。X 致力于提供一套“机制,而不是策略”,以支持一套极端通用的图形操作,从而把使用工具箱和界面的“观感”(策略)推后到应用层。
Unix 其它系统级的服务也有类似的倾向:行为的最终逻辑被尽可能推后到使用端。Unix用户可以在多种shell 中进行选择。而Unix 应用程序通常会提供很多的行为选项和令人眼花缭乱的定制功能。
这种倾向也反映出Unix的遗风:原本是为技术人员设计的操作系统;同时也表明设计的信念:最终用户永远比操作系统设计人员更清楚他们究竟需要什么。

然而这种选择机制而不是策略的代价是:当用户“可以”自己设置策略时,他们其实是“必须”自己设置策略。非技术型的终端用户常常会被Unix 丰富的选项和接口风格搞得晕头转向,于是转而选择那些伪称能够给他们提供简洁性的操作系统。
只看眼前的话,Unix 的这种自由放纵主义风格会让它失去很多非技术型用户。但从长远考虑,最终你会发觉这个“错误”换来至关重要的优势:策略相对短寿,而机制才会长存。现今流 行的界面观感常常会变成明日进化的死胡同(去问问那些使用已经过时的X 工具包的用户,他们会有一肚子苦水倒给你!)。说来说去,只提供机制不提供方针的哲学能使Unix 长久保鲜;而那些被束缚在一套方针或界面风格内的操作系统,也许早就从人们的视线中消失了。

虽然成型于TOPS-10 的TCP/IP 标准(互联网的基础) 在理论上可以与Unix 分开, 但当应用在其它操作系统上时,一直都饱受兼容性差、不稳定、bug 太多等问题的困扰。实际上,理论和规格说明人人都可以获取, 但是只有Unix 世界中你才见得到这些稳固可靠的现实成果。

从头到脚的灵活性:
许多操作系统自诩比起Unix 来有多么的“现代”,用户界面又是多么的“友好”。 它们漂亮外表的背后,却是以貌似精巧实则脆弱狭隘难用的编程接口,把用户和开发者禁锢在单一的界面方针下。在这样的操作系统中,完成设计者(指操作系统) 预见的任务很容易,但如果要完成设计者没有预料到的任务,用户不是无计可施就是痛苦不堪。
相反,Unix 具有非常彻底的灵活性。Unix 提供众多的程序粘合手段,这意味着Unix基本工具箱的各种组件连纵开合后,将收到单个工具设计者无法想象的功效。
Unix 支持多种风格的程序界面(通常也因为给终端用户增加了明显的系统复杂度而被视为Unix 的一个缺点),从而增加了它的灵活性;只管简单数据处理的程序而无需背上精巧图形界面的担子。
Unix 传统将重点放在尽力使各个程序接口相对小巧、简洁和正交——这也是另一个提高灵活性的方面。整个Unix 系统,容易的事还是那么容易,困难的事呢,至少是有可能做到的。

Unix 哲学起源于Ken Thompson 早期关于如何设计一个服务接口简洁、小巧精干的操作系统的思考,随着Unix 文化在学习如何尽可能发掘Thompson 设计思想的过程中不断成长,同时一路上还从其它许多地方博采众长。

Unix 管道的发明人、Unix 传统的奠基人之一Doug McIlroy 在[McIlroy78]中曾经说过:

(i)让每个程序就做好一件事。如果有新任务,就重新开始,不要往原程序中加入
新功能而搞得复杂。
(ii)假定每个程序的输出都会成为另一个程序的输入,哪怕那个程序还是未知的。
输出中不要有无关的信息干扰。避免使用严格的分栏格式和二进制格式输入。不要坚持
使用交互式输入。
(ii)尽可能早地将设计和编译的软件投入试用, 哪怕是操作系统也不例外,理想情
况下, 应该是在几星期内。对拙劣的代码别犹豫,扔掉重写。
(iv)优先使用工具而不是拙劣的帮助来减轻编程任务的负担。工欲善其事,必先
利其器。

Unix 哲学是这样的:一个程序只做一件事,并做好。程序要能协作。程序要能处理文本流,因为这是最通用的接口。

Rob Pike, 最伟大的C 语言大师之一, 在《Notes on C Programming》中从另一个稍微不同的角度表述了Unix 的哲学[Pike]:

原则1: 你无法断定程序会在什么地方耗费运行时间。瓶颈经常出现在想不到的地方,所以别急于胡乱找个地方改代码,除非你已经证实那儿就是瓶颈所在。
原则2:估量。在你没对代码进行估量,特别是没找到最耗时的那部分之前,别去优化速度。
原则3: 花哨的算法在n 很小时通常很慢,而n 通常很小。花哨算法的常数复杂度很大。除非你确定n 总是很大,否则不要用花哨算法(即使n 很大,也优先考虑原则2)。
原则4:花哨的算法比简单算法更容易出bug、更难实现。尽量使用简单的算法配合简单的数据结构。
原则5:数据压倒一切。如果已经选择了正确的数据结构并且把一切都组织得井井有条,正确的算法也就不言自明。编程的核心是数据结构,而不是算法6。
原则6:没有原则6。

Ken Thompson——Unix 最初版本的设计者和实现者,禅宗偈语般地对Pike 的原则4作了强调:
拿不准就穷举。

Unix 哲学中更多的内容不是这些先哲们口头表述出来的,而是由他们所作的一切和Unix 本身所作出的榜样体现出来的。从整体上来说,可以概括为以下几点:
1. 模块原则:使用简洁的接口拼合简单的部件。
2. 清晰原则:清晰胜于机巧。
3. 组合原则:设计时考虑拼接组合。
4. 分离原则:策略同机制分离,接口同引擎分离。
5. 简洁原则:设计要简洁,复杂度能低则低。
6. 吝啬原则:除非确无它法,不要编写庞大的程序。
7. 透明性原则:设计要可见,以便审查和调试。
8. 健壮原则:健壮源于透明与简洁。
9. 表示原则:把知识叠入数据以求逻辑质朴而健壮。
10. 通俗原则:接口设计避免标新立异。
11. 缄默原则:如果一个程序没什么好说的,就沉默。
12. 补救原则:出现异常时,马上退出并给出足够错误信息。
13. 经济原则:宁花机器一分,不花程序员一秒。
14. 生成原则:避免手工hack,尽量编写程序去生成程序。
15. 优化原则:雕琢前先要有原型,跑之前先学会走。
16. 多样原则:决不相信所谓“不二法门”的断言。
17. 扩展原则:设计着眼未来,未来总比预想来得快。

维护如此重要而成本如此高昂;在写程序时,要想到你不是写给执行代码的计算机看的,而是给人——将来阅读维护源码的人,包括你自己——看的。

接口:

如果程序彼此之间不能有效通信,那么软件就难免会陷入复杂度的泥淖。
在输入输出方面,Unix 传统极力提倡采用简单、文本化、面向流、设备无关的格式。
在经典的Unix 下,多数程序都尽可能采用简单过滤器的形式,即将一个输入的简单文本流处理为一个简单的文本流输出。
抛开世俗眼光,Unix 程序员偏爱这种做法并不是因为他们仇视图形用户界面,而是因为如果程序不采用简单的文本输入输出流,它们就极难衔接。

要想让程序具有组合性,就要使程序彼此独立。在文本流这一端的程序应该尽可能不要考虑文本流另一端的程序。将一端的程序替换为另一个截然不同的程序,而完全不惊扰另一端应该很容易做到。

GUI 可以是个好东西。有时竭尽所能也不可避免复杂的二进制数据格式。但是,在做一个GUI 前,最好还是应该想想可不可以把复杂的交互程序跟干粗活的算法程序分离开,每个部分单独成为一块,然后用一个简单的命令流或者是应用协议将其组合在一起。

在构思精巧的数据传输格式前,有必要实地考察一下,是否能利用简单的文本数据格式;以一点点格式解析的代价,换得可以使用通用工具来构造或解读数据流的好 处是值得的。当程序无法自然地使用序列化、协议形式的接口时,正确的Unix 设计至少是,把尽可能多的编程元素组织为一套定义良好的API。这样,至少你可以通过链接调用应用程序,或者可以根据不同任务的需求粘合使用不同的接口。

将策略同机制剥离,就有可能在探索新策略的时候不足以打破机制。另外,我们也可以更容易为机制写出较好的测试(因为策略太短命,不值得花太多精力在这上面)。

一个方法是将应用程序分成可以协作的前端和后端进程,通过套接字上层的专用应用协议进行通讯;前端实现策略,后端实现机制。比起仅用单个进程的整体实现方式来说,
这种双端设计方式大大降低了整体复杂度,bug 有望减少,从而降低程序的寿命周期成本。

来自多方面的压力常常会让程序变得复杂(由此代价更高,bug 更多),其中一种压力就是来自技术上的虚荣心理。程序员们都很聪明,常常以能玩转复杂东西和耍弄抽象概念的能力为傲,这一点也无可厚非。但正因如此,他们 常常会与同行们比试,看看谁能够鼓捣出最错综复杂的美妙事物。正如我们经常所见,他们的设计能力大大超出他们的实现和排错能力,结果便是代价高昂的废品。

“错综复杂的美妙事物”听来自相矛盾。Unix 程序员相互比的是谁能够做到“简洁而漂亮”并以此为荣,这一点虽然只是隐含在这些规则之中,但还是很值得公开提出来强调一下。
—Doug McIlroy

吝啬原则: 除非确无它法,不要编写庞大的程序。
“大”有两重含义:体积大,复杂程度高。程序大了,维护起来就困难。由于人们
对花费了大量精力才做出来的东西难以割舍,结果导致在庞大的程序中把投资浪费在注
定要失败或者并非最佳的方案上.

大多数软件禁不起磕碰,毛病很多,就是因为过于复杂,很难通盘考虑。如果不能够正确理解一个程序的逻辑,就不能确信其是否正确,也就不能在出错的时候修复它。
这也就带来了让程序健壮的方法,就是让程序的内部逻辑更易于理解。要做到这一点主要有两种方法:透明化和简洁化。
软件的透明性就是指一眼就能够看出来是怎么回事。如果人们不需要绞尽脑汁就能够推断出所有可能的情况,那么这个程序就是简洁的。程序越简洁,越透明,也就越健壮.

数据要比编程逻辑更容易驾驭。所以接下来,如果要在复杂数据和复杂代码中选择一个,宁愿选择前者。更进一步:在设计中,你应该主动将代码的复杂度转移到数据之中去。

最易用的程序就是用户需要学习新东西最少的程序——或者,换句话说,最易用的程序就是最切合用户已有知识的程序。
因此,接口设计应该避免毫无来由的标新立异和自作聪明。如果你编制一个计算器程序,‘+’应该永远表示加法。而设计接口的时候,尽量按照用户最可能熟悉的同样功能接口和相似应用程序来进行建模。

Unix 中最古老最持久的设计原则之一就是:若程序没有什么特别之处可讲,就保持沉默。行为良好的程序应该默默工作,决不唠唠叨叨,碍手碍脚。沉默是金。

我认为简洁是Unix 程序的核心风格。一旦程序的输出成为另一个程序的输入,就很容易把需要的数据挑出来。站在人的角度上来说――重要信息不应该混杂在冗长的程序内部行为信息中。如果显示的信息都是重要的,那就不用找了。
—Ken Arnold

借助原型化找出哪些功能不必实现,有助于对性能进行优化;那些不用写的代码显然无需优化。目前,最强大的优化工具恐怕就是delete 键了。

我最有成效的一天就是扔掉了1000 行代码。
–Ken Thompson

蜀门在线ShuMenOL.com

Posted by – February 19, 2010

不错的一款国内2.8D网游, 注册地址:

http://qinyou.shumenol.com/view/375648/38