必读网 - 人生必读的书

TXT下载此书 | 书籍信息


(双击鼠标开启屏幕滚动,鼠标上下控制速度) 返回首页
选择背景色:
浏览字体:[ ]  
字体颜色: 双击鼠标滚屏: (1最慢,10最快)

程序员修炼之道

_2 Andrew(美)
  遗憾的是,几乎再没有简单的答案了。但拥有大量知识资产,并把批判的分析应用于你将要阅读的技术出版物的洪流,你将能够理解复杂的答案。
与古鲁打交道的礼节与教养
  随着Internet在全球普及,古鲁们突然变得像你的Enter键一样贴近。那么,你怎样才能找到一个古鲁,怎样才能找一个古鲁和你交谈呢?
  我们找到了一些简单的诀窍。
l 确切地知道你想要问什么,并尽量明确具体。
l 小心而得体地组织你的问题。记住你是在请求帮助;不要显得好像是在要求对方回答。
l 组织好问题之后,停下来,再找找答案。选出一些关键字,搜索Web。查找适当的FAQ(常见问题的解答列表)。
l 决定你是想公开提问还是私下提问。Usenet新闻组是与专家会面的美妙场所,在那里可以讨论几乎任何问题,但有些人对这些新闻组的公共性质有顾虑。你总是可以用另外的方法:直接发电子邮件给古鲁。不管怎样,要使用有意义的主题(“需要帮助!!!”无益于事)。
l 坐回椅子上,耐心等候。人们很忙,也许需要几天才能得到明确的答案。
  最后,请一定要感谢任何回应你的人。如果你看到有人提出你能够解答的问题,尽你的一份力,参与解答。
挑战
l 这周就开始学习一种新语言。总在用C++编程?试试Smalltalk[URL 13]或Squeak[URL 14]。在用Java?试试Eiffel[URL 10]或TOM[URL 15]。关于其他自由编译器和环境的来源,参见267页。
l 开始阅读一本新书(但要先读完这一本!)。如果你在进行非常详细的实现和编码,就阅读关于设计和架构的书。如果你在进行高级设计,就阅读关于编码技术的书。
l 出去和与你的当前项目无关的人、或是其他公司的人谈谈技术。在你们公司的自助餐厅里结识其他人,或是在本地用户组织聚会时寻找兴趣相投的人。
6 交流!
我相信,被打量比被忽略要好。
  ——Mae West, Belle of the Nineties,1934
  也许我们可以从West女士那里学到一点什么。问题不只是你有什么,还要看你怎样包装它。除非你能够与他人交流,否则就算你拥有最好的主意、最漂亮的代码、或是最注重实效的想法,最终也会毫无结果。没有有效的交流,一个好想法就只是一个无人关心的孤儿。
  作为开发者,我们必须在许多层面上进行交流。我们把许多小时花在开会、倾听和交谈上。我们与最终用户一起工作,设法了解他们的需要。我们编写代码,与机器交流我们的意图;把我们的想法变成文档,留给以后的开发者。我们撰写提案和备忘录,用以申请资源并证明其正当性、报告我们的状态、以及提出各种新方法。我们每天在团队中工作,宣扬我们的主意、修正现有的做法、并提出新的做法。我们的时间有很大一部分都花在交流上,所以我们需要把它做好。
  我们汇总了我们觉得有用的一些想法。
知道你想要说什么
  在工作中使用的更为正式的交流方式中,最困难的部分也许是确切地弄清楚你想要说什么。小说家在开始写作之前,会详细地构思情节,而撰写技术文档的人却常常乐于坐到键盘前,键入“1. 介绍……”,并开始敲入接下来在他们的头脑里冒出来的任何东西。
  规划你想要说的东西。写出大纲。然后问你自己:“这是否讲清了我要说的所有内容?”提炼它,直到确实如此为止。
  这个方法不只适用于撰写文档。当你面临重要会议、或是要与重要客户通电话时,简略记下你想要交流的想法,并准备好几种把它们讲清楚的策略。
了解你的听众
  只有当你是在传达信息时,你才是在交流。为此,你需要了解你的听众的需要、兴趣、能力。我们都曾出席过这样的会议:一个做开发的滑稽人物在发表长篇独白,讲述某种神秘技术的各种优点,把市场部副总裁弄得目光呆滞。这不是交流,而只是空谈,让人厌烦的(annoying)空谈。
  要在脑海里形成一幅明确的关于你的听众的画面。下一页的图1.1中显示的WISDOM离合诗(acrostic)可能会对你有帮助。
  假设你想提议开发一个基于Web的系统,用于让你们的最终用户提交bug报告。取决于听众的不同,你可以用不同的方式介绍这个系统。如果可以不用在电话上等候,每天24小时提交bug报告,最终用户将会很高兴。你们的市场部门可以利用这一事实促销。支持部门的经理会因为两个原因而高兴:所需员工更少,问题报告得以自动化。最后,开发者会因为能获得基于Web的客户-服务器技术和新数据库引擎方面的经验而感到享受。通过针对不同的人进行适当的修正,你将让他们都为你的项目感到兴奋。
选择时机
  这是星期五的下午六点,审计人员进驻已有一周。你的老板最小的孩子在医院里,外面下着滂沱大雨,这时开车回家肯定是一场噩梦。这大概不是向她提出PC内存升级的好时候。
  为了了解你的听众需要听到什么,你需要弄清楚他们的“轻重缓急”是什么。找到一个刚刚因为丢失源码而遭到老板批评的经理,向她介绍你关于源码仓库的构想,你将会拥有一个更容易接纳的倾听者。要让你所说的适得其时,在内容上切实相关。有时候,只要简单地问一句“现在我们可以谈谈……吗?”就可以了。
图1.1 WISDOM离合诗——了解听众
What do you want them to learn?
What is their interest in what you’ve got to say?
How sophisticated are they?
How much detail do they want?
Whom do you want to own the information?
How can you motivate them to listen to you?
你想让他们学到什么?
他们对你讲的什么感兴趣?
他们有多富有经验?
他们想要多少细节?
你想要让谁拥有这些信息?
你如何促使他们听你说话?
选择风格
  调整你的交流风格,让其适应你的听众。有人要的是正式的“事实”简报。另一些人喜欢在进入正题之前高谈阔论一番。如果是书面文档,则有人喜欢一大摞报告,而另一些人却喜欢简单的备忘录或电子邮件。如果有疑问,就询问对方。
  但是,要记住,你也是交流事务的一方。如果有人说,他们需要你用一段话进行描述,而你觉得不用若干页纸就无法做到,如实告诉他们。记住,这样的反馈也是交流的一种形式。
让文档美观
  你的主意很重要。它们应该以美观的方式传递给你的听众。
  太多程序员(和他们的经理)在制作书面文档时只关心内容。我们认为这是一个错误。任何一个厨师都会告诉你,你可以在厨房里忙碌几个小时,最后却会因为饭菜糟糕的外观而毁掉你的努力。
  在今天,已经没有任何借口制作出外观糟糕的打印文档。现代的字处理器(以及像LaTeX和troff这样的排版系统)能够生成非常好的输出。你只需要学习一些基本的命令。如果你的字处理器支持样式表,就加以利用(你的公司也许已经定义了你可以使用的样式表)。学习如何设置页眉和页脚。查看你的软件包中包含的样本文档,以对样式和版式有所了解。检查拼写,先自动,再手工。毕竟,有一些拼写错误是检查器找不出来的(After awl, their are spelling miss streaks that the chequer can knot ketch)。
让听众参与
  我们常常发现,与制作文档的过程相比,我们制作出的文档最后并没有那么重要。如果可能,让你的读者参与文档的早期草稿的制作。获取他们的反馈,并汲取他们的智慧。你将建立良好的工作关系,并很可能在此过程中制作出更好的文档。
做倾听者
  如果你想要大家听你说话,你必须使用一种方法:听他们说话。即使你掌握着全部信息,即使那是一个正式会议,你站在20个衣着正式的人面前——如果你不听他们说话,他们也不会听你说话。
  鼓励大家通过提问来交谈,或是让他们总结你告诉他们的东西。把会议变成对话,你将能更有效地阐明你的观点。谁知道呢,你也许还能学到点什么。
回复他人
  如果你向别人提问,他们不做出回应,你会觉得他们不礼貌。但当别人给你发送电子邮件或备忘录、请你提供信息、或是采取某种行动时,你是否经常忘记回复?在匆忙的日常生活中,很容易忘记事情。你应该总是对电子邮件和语音邮件做出回应,即使内容只是“我稍后回复你。”随时通知别人,会让他们更容易原谅你偶然的疏忽,并让他们觉得你没有忘记他们。
提示10
It’s Both What You Say and the Way You Say It
你说什么和你怎么说同样重要
  除非你生活在真空中,你才不需要能交流。交流越有效,你就越有影响力。
电子邮件交流
  我们所说的关于书面交流的所有东西都同样适用于电子邮件。现在的电子邮件已经发展成为公司内部和公司之间进行交流的主要手段。它被用于讨论合约、调解争端,以及用作法庭证据。但因为某种原因,许多从不会发出低劣的书面文档的人却乐于往全世界乱扔外观糟糕的电子邮件。
  我们关于电子邮件的提示很简单:
l 在你按下SEND之前进行校对。
l 检查拼写。
l 让格式保持简单。有人使用均衡字体(proportional font)阅读电子邮件,所以你辛苦制作的ASCII艺术图形在他们看来将像是母鸡的脚印一样乱七八糟。
l 只在你知道对方能够阅读rich-text或HTML格式的邮件的情况下使用这些格式。纯文本是通用的。
l 设法让引文减至最少。没有人喜欢收到一封回邮,其中有100行是他原来的电子邮件,只在最后新添了三个字:“我同意”。
l 如果你引用别人的电子邮件,一定要注明出处。并在正文中进行引用(而不是当做附件)。
l 不要用言语攻击别人(flame),除非你想让别人也攻击你,并老是纠缠你。
l 在发送之前检查你的收件人名单。最近《华尔街日报》上有一篇文章报道说,有一个雇员通过部门的电子邮件散布对老板的不满,却没有意识到老板也在收件人名单里。
l 将你的电子邮件——你收到的重要文件和你发送的邮件——加以组织并存档。
  如Microsoft和Netscape的好些雇员在1999年司法部调查期间所发现的,e-mail是永久性的。要设法像对待任何书面备忘录或报告一样小心对待e-mail。
总结
l 知道你想要说什么。
l 了解你的听众。
l 选择时机。
l 选择风格。
l 让文档美观。
l 让听众参与。
l 做倾听者。
l 回复他人。
相关内容:
l 原型与便笺,53页
l 注重实效的团队,224页
挑战
l 有几本好书讨论了开发团队内部的交流[Bro95, McC95, DL99]。下决心在接下来的18个月里读完所有这三本书。此外,Dinosaur Brains[Ber96]这本书讨论了我们所有人都会带到工作环境中的“情绪包袱”。
l 在你下一次进行展示、或是撰写备忘录支持某种立场时,先试着按第20页的WISDOM离合诗做一遍。看这样是否有助于你了解怎样定位你的讲话。如果合适,事后与你的听众谈一谈,看你对他们的需要的估计有多准确。
7 重复的危害
有些提示和诀窍可应用于软件开发的所有层面,有些想法几乎是公理,有些过程实际上普遍适用。但是,人们几乎没有为这些途径建立这样的文档,你很可能会发现,它们作为零散的段落写在关于设计、项目管理或编码的讨论中。
  在这一章里,我们将要把这些想法和过程集中在一起。头两节,“重复的危害”与“正交性”,密切相关。前者提醒你,不要在系统各处对知识进行重复,后者提醒你,不要把任何一项知识分散在多个系统组件中。
  随着变化的步伐加快,我们越来越难以让应用跟上变化。在“可撤消性”中,我们将考察有助于使你的项目与其不断变化的环境绝缘的一些技术。
  接下来的两节也是相关的。在“曳光弹”中,我们将讨论一种开发方式,能让你同时搜集需求、测试设计、并实现代码。这听起来太好,不可能是真的?的确如此:曳光弹开发并非总是可以应用。“原型与便笺”将告诉你,在曳光弹开发不适用的情况下,怎样使用原型来测试架构、算法、接口以及各种想法。
  随着计算机科学慢慢成熟,设计者正在制作越来越高级的语言。尽管能够接受“让它这样”(make it so)指令的编译器还没有发明出来,在“领域语言”中我们给出了一些适度的建议,你可以自行加以实施。
  最后,我们都是在一个时间和资源有限的世界上工作。如果你善于估计出事情需要多长时间完成,你就能更好地在两者都很匮乏的情况下生存下去(并让你的老板更高兴)。我们将在“估算”中涵盖这一主题。
  在开发过程中牢记这些基本原则,你就将能编写更快、更好、更强健的代码。你甚至可以让这看起来很容易。
7 重复的危害
  给予计算机两项自相矛盾的知识,是James T. Kirk舰长(出自Star Trek,“星际迷航”——译注)喜欢用来使四处劫掠的人工智能生命失效的方法。遗憾的是,同样的原则也能有效地使你的代码失效。
  作为程序员,我们收集、组织、维护和利用知识。我们在规范中记载知识、在运行的代码中使其活跃起来并将其用于提供测试过程中所需的检查。
  遗憾的是,知识并不稳定。它变化——常常很快。你对需求的理解可能会随着与客户的会谈而发生变化。政府改变规章制度,有些商业逻辑过时了。测试也许表明所选择的算法无法工作。所有这些不稳定都意味着我们要把很大一部分时间花在维护上,重新组织和表达我们的系统中的知识。
  大多数人都以为维护是在应用发布时开始的,维护就意味着修正bug和增强特性。我们认为这些人错了。程序员须持续不断地维护。我们的理解逐日变化。当我们设计或编码时,出现了新的需求。环境或许变了。不管原因是什么,维护都不是时有时无的活动,而是整个开发过程中的例行事务。
  当我们进行维护时,我们必须找到并改变事物的表示——那些嵌在应用中的知识胶囊。问题是,在我们开发的规范、过程和程序中很容易重复表述知识,而当我们这样做时,我们是在向维护的噩梦发出邀请——在应用发布之前就会开始的噩梦。
  我们觉得,可靠地开发软件、并让我们的开发更易于理解和维护的惟一途径,是遵循我们称之为DRY的原则:
  系统中的每一项知识都必须具有单一、无歧义、权威的表示。
  我们为何称其为DRY?
提示11
DRY – Don’t Repeat Yourself
不要重复你自己
  与此不同的做法是在两个或更多地方表达同一事物。如果你改变其中一处,你必须记得改变其他各处。或者,就像那些异形计算机,你的程序将因为自相矛盾而被迫屈服。这不是你是否能记住的问题,而是你何时忘记的问题。
  你会发现DRY原则在全书中一再出现,并且常常出现在与编码无关的语境中。我们觉得,这是注重实效的程序员的工具箱里最重要的工具之一。
  在这一节我们将概述重复的问题,并提出对此加以处理的一般策略。
重复是怎样发生的
  我们所见到的大多数重复都可归入下列范畴:
l 强加的重复(imposed duplication)。开发者觉得他们无可选择——环境似乎要求重复。
l 无意的重复(inadvertent duplication)。开发者没有意识到他们在重复信息。
l 无耐性的重复(impatient duplication)。开发者偷懒,他们重复,因为那样似乎更容易。
l 开发者之间的重复(interdeveloper duplication)。同一团队(或不同团队)的几个人重复了同样的信息。
让我们更详细地看一看这四个以“i ”开头的重复。
强加的重复
  有时,重复似乎是强加给我们的。项目标准可能要求建立含有重复信息的文档,或是重复代码中的信息的文档。多个目标平台各自需要自己的编程语言、库以及开发环境,这会使我们重复共有的定义和过程。编程语言自身要求某些重复信息的结构。我们都在我们觉得无力避免重复的情形下工作过。然而也有一些方法,可用于把一项知识存放在一处,以遵守DRY原则,同时也让我们的生活更容易一点。这里有一些这样的技术:
信息的多种表示。在编码一级,我们常常需要以不同的形式表示同一信息。我们也许在编写客户-服务器应用,在客户和服务器端使用了不同的语言,并且需要在两端都表示某种共有的结构。我们或许需要一个类,其属性是某个数据库表的schema(模型、方案)的镜像。你也许在撰写一本书,其中包括的程序片段,也正是你要编译并测试的程序。
  发挥一点聪明才智,你通常能够消除重复的需要。答案常常是编写简单的过滤器或代码生成器。可以在每次构建(build)软件时,使用简单的代码生成器,根据公共的元数据表示构建多种语言下的结构(示例参见图3.4,106页)。可以根据在线数据库schema、或是最初用于构建schema的元数据,自动生成类定义。本书中摘录的代码,由预处理器在我们每次对文本进行格式化时插入。诀窍是让该过程成为主动的,这不能是一次性转换,否则我们就会退回到重复数据的情况。
代码中的文档。程序员被教导说,要给代码加上注释:好代码有许多注释。遗憾的是,没有人教给他们,代码为什么需要注释:糟糕的代码才需要许多注释。
  DRY法则告诉我们,要把低级的知识放在代码中,它属于那里;把注释保留给其他的高级说明。否则,我们就是在重复知识,而每一次改变都意味着既要改变代码,也要改变注释。注释将不可避免地变得过时,而不可信任的注释比完全没有注释更糟(关于注释的更多信息,参见全都是写,248页)。
文档与代码。你撰写文档,然后编写代码。有些东西变了,你修订文档、更新代码。文档和代码都含有同一知识的表示。而我们都知道,在最紧张的时候——最后期限在逼近,重要的客户在喊叫——我们往往会推迟文档的更新。
  Dave曾经参与过一个国际电报交换机项目的开发。很容易理解,客户要求提供详尽的测试规范,并要求软件在每次交付时都通过所有测试。为了确保测试准确地反映规范,开发团队用程序方式、根据文档本身生成这些测试。当客户修订他们的规范时,测试套件会自动改变。有一次团队向客户证明了,该过程很健全,生成验收测试在典型情况下只需要几秒种。
语言问题。许多语言会在源码中强加可观的重复。如果语言使模块的接口与其实现分离,就常常会出现这样的情况。C与C++有头文件,在其中重复了被导出变量、函数和(C++的)类的名称和类型信息。Object Pascal甚至会在同一文件里重复这些信息。如果你使用远地过程调用或CORBA[URL 29],你将会在接口规范与实现它的代码之间重复接口信息。
  没有什么简单的技术可用于克服语言的这些需求。尽管有些开发环境通过自动生成头文件、隐藏了对头文件的需要,而Object Pascal允许你缩写重复的函数声明,你通常仍受制于给予你的东西。至少对于大多数与语言有关的问题,与实现不一致的头文件将会产生某种形式的编译或链接错误。你仍会弄错事情,但至少,你将在很早的时候就得到通知。
  再思考一下头文件和实现文件中的注释。绝对没有理由在这两种文件之间重复函数或类头注释(header comment)。应该用头文件记载接口问题,用实现文件记载代码的使用者无须了解的实际细节。
无意的重复
  有时,重复来自设计中的错误。
  让我们看一个来自配送行业的例子。假定我们的分析揭示,一辆卡车有车型、牌照号、司机及其他一些属性。与此类似,发运路线的属性包括路线、卡车和司机。基于这一理解,我们编写了一些类。
  但如果Sally打电话请病假、我们必须改换司机,事情又会怎样呢?Truck和DeliverRoute都包含有司机。我们改变哪一个?显然这样的重复很糟糕。根据底层的商业模型对其进行规范化(normalize)——卡车的底层属性集真的应包含司机?路线呢?又或许我们需要第三种对象,把司机、卡车及路线结合在一起。不管最终的解决方案是什么,我们都应避免这种不规范的数据。
  当我们拥有多个互相依赖的数据元素时,会出现一种不那么显而易见的不规范数据。让我们看一个表示线段的类:
class Line {
public:
Point start;
Point end;
double length;
};
  第一眼看上去,这个类似乎是合理的。线段显然有起点和终点,并总是有长度(即使长度为零)。但这里有重复。长度是由起点和终点决定的:改变其中一个,长度就会变化。最好是让长度成为计算字段:
class Line {
public:
Point start;
Point end;
double length() { return stanceTo(end); }
};
  在以后的开发过程中,你可以因为性能原因而选择违反DRY原则。这经常会发生在你需要缓存数据,以避免重复昂贵的操作时。其诀窍是使影响局部化。对DRY原则的违反没有暴露给外界:只有类中的方法需要注意“保持行为良好”。
class Line {
private:
bool changed;
double length;
Point start;
Point end;
public:
void setStart(Point p) { start = p; changed = true; }
void setEnd(Point p) { end = p; changed = true; }
Point getStart(void) { return start; }
Point getEnd(void) { return end; }
double getLength() {
if (changed) {
length = stanceTo(end);
changed = false;
}
return length;
返回书籍页