振华 的个人资料thinkpad照片日志列表更多 ![]() | 帮助 |
|
3月22日 微软这样做测试--吃自己做的狗食告诉你微软如何从卓越走向卓越的冰山一角!原文地址
看看,看看!
----------------------------------------------------------------------------------------------------------------------------------
当你在阅读这篇文章的时候,世界上有数万名外部Beta测试用户正在检验最新的微软软件。但是,很少有用户能够想到,这家软件巨头拥有的最大试验对象,其实就是它自己。在某个软件的最终正式版本发布的很久很久以前,微软公司在内部就已经开始对它进行使用测试了。微软公司将这种测试的工序戏称为“吃自己做的狗食(eating its own dog food)”。
微软内部测试体系:“吃狗食(eating its own dog food)”
最近,我们非常有幸地采访到了微软的首席信息官员(CIO)Ron Markezich先生,这位常常被称为“微软终极Beta测试者”的神奇人物。他将和我们一起,带领诸位读者深入了解一下微软公司是怎样通过“吃自己做的狗食”,来最终塑造好全球每天有数亿用户都在使用的常用软件的。
作为微软公司的CIO,Markezich先生领导着微软公司的IT部门。这个部门要负责管理微软那由遍布全球超过63000名雇员所支持的庞大组织结构。也许更为重要的是,他的团队要在软件产品的研发中扮演一个至关重要的角色,他们需要在微软软件产品完成前,就不断地试用它,尽力去发现软件中所存在的错误,并向开发团队提供意见反馈。
要想找到个人电脑用户来测试像 Windows Vista 之类的软件,这非常的容易;即便是Google也经常将自己的软件作为测试版本向用户推出。但是在商业领域,情形就完全不一样了。商业公司要求自己所依赖的软件系统必须在任何恶劣的环境下都运转良好,软件中所潜在的任何微小瑕疵,都有可能导致巨大的经济损失。为了走出这种困境,微软曾经试图通过发布“体验版(go live)”的授权,来鼓励自己的商业客户安装处于测试阶段的企业软件。但是,其后的结果让微软明白了一个道理:“如果你想要理直气壮地向自己的客户推销一个新款的软件,唯一的方法便是在它初次发布前,就先在自己公司的内部广泛地进行试用。”
微软终极Beta测试者:Ron Markezich
这个事件是在微软推出Exchange Server 2000时发生的。Markezich先生回忆,当时微软公司在自己内部服务器上都还没有全面部署这个软件,就正式将它出售给了商业顾客。结果带来的却是彻彻底底的一个恶梦:微软的顾客经历了每一个Beta测试者都会经历的无数软件问题,变得非常的愤怒;而微软公司则不得不在仓促之间推出了无数的软件补丁。
Markezich先生解释到:“在这件事之后,我们就发誓,除非我们已经在微软自己的生意上运行某一件软件产品了,否则我们就不会将这件产品卖给自己的企业客户。”从那时起,“吃狗食”计划就扩展到了各种各样的微软产品之上,一直到它现在成为了一个产品在最终发布前所必须经历的质量检测程序。
Markezich先生说:“我们不光是想参与到软件的Beta版本的测试当中,同时我们也会参与到软件产品的计划、设计、生产需求,乃至其整个产品生命周期的方方面面,在每个阶段都对软件的生产部门给予反馈。”
不像一名典型的Beta测试者,Markezich的团队在新产品发布问题上经常摇摆不定。事实上,作为“吃狗食”程序的一部分,他有权力推迟某个软件产品正式投入生产(RTM)的时间,如果该软件产品不能符合开发团队先前所定下的目标和期望的话。
“如果在某一个阶段,我们没有达到这个阶段所预定的目标,我们将不会允许这个产品的开发团队进入到下一个阶段,这都是为了最终产品能够正式投产。如果我们无法在软件正式发布前的一年,甚至是一年半到两年,就达到正式投产时所需要的标准,我们就不会允许这个产品按时投入市场。”Markezich先生说。
谈到Windows Vista,Markezich先生和我们分享了微软对Vista的部分商业导向功能的期望,其中包括BitLocker、企业搜索、强力用户身份验证、网络连接保护等等。他还补充到“这个检测体系是软件开发团队的延伸;在每件产品正式发布前,我们都要为它的质量作出担保,并签上自己的大名。”
“吃狗食”程序开始于软件的开发团队,他们需要在软件的alpha阶段对软件进行试用;然后,项目负责人会通过微软公司的内部广告和知识竞赛来邀请志愿者加入Beta版本的测试。一段时间后,你就可以发现在微软公司内部有着无数的员工正在试用这些“发布前版本”软件了。
曾经在Windows XP Service Pack 2进行内部测试时,微软公司内有超过60000台电脑运行着这一系统。Markezich希望在Vista正式发布前,至少有同样数目的电脑在测试Vista。根据 Markezich统计,至今年6月中旬,微软内部有超过16000名的Windows Vista Beta 2 测试者,以及25000名Office 2007 Beta 2的测试者。
“每一件产品,我们都有一个专门的团队在负责管理它的测试过程。”Markezich先生说。“当我们开始Vista Beta 2的测试时,我们会冲到走廊上大声地喊:‘嘿!伙计们,快来抢新出炉的Vista Beta 2!’这个工作的角色一半像是传教士,你要召唤大家来试用新的产品;而另一半却像传声筒,你需要给产品的开发团队收集和整理反馈信息--最终,我们将会决定究竟是哪一个版本的软件会正式投向市场。”
微软Windows和Office在最终公开测试前,早已经进行了广泛的内部测试。
一般来讲,一个软件产品从最初的 alpha 阶段到最终正式生产(RTM),会经历8个不同阶段的测试。然后,在最终正式生产后,微软公司还将全面的在公司内部部署这个软件产品。虽然现在Markezich监督数量巨大的微软产品同时进行“吃狗食”的检测,但他还是处理的有条不紊。除了前面提到的Vista和Office外,现在还有在Exchange 12上运行的6000个电子邮箱,以及超过30个运行着Longhorn Servers的网络服务器。有1000名微软的员工正在运行公司代号“石弓(Crossbow)”的下一代Windows Mobile操作系统,同时还有7000台原名“Systems Management Server v4”,现在改名叫“System Center Configuration Manager 2007”的系统正在运行。此外,还有超过3000台运行着下一代“ Microsoft Operations Manager ”的电脑也正在部署之中。
当我们请求他详细解释一下微软公司从“吃狗食”计划中所获得了什么好处的时候,Markezich先生的目光一下变得兴奋了起来。他说,他的团队不光是在保证微软产品拥有最好质量的方面起到了至关重要的作用,微软公司自身也能够在第一时间就使用上最新最好的软件了。
“曾经在我们推出 Exchange 2003 的时候,我通过‘吃狗食’测试,将运行着老版本Exchange的74台服务器,消减到了7台运行着新版Exchange的服务器。现在,我只需要4台运行着最新版本的Exchange服务器,就可以完成以前同样的工作了。”Markezich先生解释到,“因此,事实上,我‘吃狗食’,意味着我将以最快的速度在新软件上获取最多的好处。”
“我发现,我的员工和顾客最满意的时候,都是那些我们最强调‘吃狗食’测试的时候。”他强调说,“坦白说,当一个雇员发现了一个问题时,我们会非常的高兴--只要这是发生在RTM之前。”
由于对Exchange、Office和Vista产品进行“吃狗食”检测带来了显著的效果,微软公司开始着手推广它名为“7×24促进”的活动。微软声称,只要它的员工能够使用这些软件产品24个月,就能够总共为公司节省700万小时的生产时间。 Markezich笑着表示公司的官方估计还是太过于谦虚,这个结论至少还少算了1000万个小时。
但是,Markezich也承认,当你试图去推动那些有自己日常任务需要完成的公司员工来搜寻潜在的软件Bug时,事情也不总是会那么地顺利。
“很多时候,作为公司的CIO,你都会接到某个愤怒的员工向你大声投诉。他会向你大声地吼叫:‘你们的人他妈的害死我了!我本来要向客户做产品介绍的,就是因为这个该死的新软件出了问题,我就什么也做不了了!’每当这个时候,我们就只好安慰他们:‘你其实通过这件事为公司创造了更多的价值,这些价值比你能够从那个客户身上所创造的要多得多。因为你找到了一个潜在的问题,提高了产品的质量,从而使千万个其它顾客在未来不会遭遇到同样的问题了。”
当谈到未来的规划时,Markezich预计会有越来越多的产品会在微软内部进行“吃狗食”的测试。并且,他还表示在要求苛刻的商业应用上使用Beta软件,其实是非常物超所值的。
最后,他幽默地说到:“我非常急切地等待着有一天我们能够对Xbox进行‘吃狗食’的测试。虽然公司董事会至今还没有批准这一请求!”
印度是如何做软件开发的 看看印度阿三..........
--------------------------------------------------------------------------------------------------------------
我在工作中,接触到印度软件公司开发出来的软件: 整个体系架构非常清晰,按照我们的要求实现了全部功能,而且相当稳定。但是打开具体的代码一看,拖沓冗长,水平不咋样。我们自己的一些程序员就有怪话了,说他们水平真低。但是!印度人能够把软件整体把握得很好,能够完成软件,并得到相当好的设计文档。
而中国人在那里琢磨数据结构、算法,界面人员就还没编码就想着是Outlook式的还是Visual Studio式的界面。到最后就成为Code高手,对某 些特定的开发工具精通,但是就是不能保证能够把一个软件稳当、完整的开发出来。
举个简单的例子:
软件中需要一个列表,用来表示我们处理的事务。该类表在业务繁忙的时候将变得很大。中国人就用双向链表,抱着《数据结构》书在那里写 链表的类。印度人开了一个大数组,然后就开始干。为什么印度人不用链表,他们说:
1.你们给出的设备(小型机),最少具备512M内存,浪费一些没有什么。
2.数组方式访问方便、效率高。
看出了一拿到东西就吭哧吭哧作Code,和好好进行软件分析的不同了吗?正好前几天我有几个同事从印度回来和我们交流,那家公司是CMM4级 公司。我感受的几点:
1.流程重于项目
2.QC(就是QA)独立于研发部门,专门检查研发部门的开发流程是不是按照既定流程走。如果QC觉得流程不对,他会直接上报高层,项目肯定就此停止。
3.所谓的项目经理(PC)一般也是从编码人员升上来的,并不是所谓的不懂技术,一般都至少有四年以上的经验
4.PC主要就是制定开发计划,负责协调,填写各种表格。
5.所有的东西(包括草稿)都有文档。
6.详细文档要求达到只有这个文档就可以编码的程度,一般写文档时间占60%,编码时间极少
7.有各种详细的review(同行评审),项目组内的,项目组之间的,客户的……
8.计划很详细,的确能达到小时级,但是实际情况还是误差比较大,所以他们也有加班。
先学习UML和Rose以及RUP,不要总是要找着证据。在中国的软件开发水平下,很难给你一个好的例子,OK?中国人总是要看到一个东西有了试验田,而且稻子长得好,才换稻种。要知道在国外上述的软件开发模式的应用,大可以看看 Rational网页上的story。Just do it!一句话,中国的软件开发水平低得很。赶不上印度人,印度的软件公司可以让高中生编代码,它的软件工程水平可想而知。
当然,你如果是个很牛的程序员。估计够呛,因为中国的气氛中,很牛的程序员都很难接受软件工程的。你可以测试一下自己,看看自己适不适合现在学习软件工程:
1.你是不是不能忍受一个编程序不如你的人做你的项目经理?
2.你是不是觉得你的老板对客户吹牛皮、夸大自己而感到不舒服? 3.你是不是一个拿到一个需求脑袋里第一念头就是如何实现的人? 4.你是不是很崇拜Stallman,Linus,很讨厌Microsoft? 5.你是不是曾经在深夜编码的时候,突然感觉到一种乏味,对Code的生涯感到一种无趣?我们现在处于深深的自卑当中,感到中国的软件工程水平的低下已经是牵涉到民族劣根性的问题了。 1.他们的软件教育水平:我们招聘印度人,给应聘者出了一份与国内差不多的试卷,有基础概念和编程题目。等到他们完成后,我们这些中国 的自认高手惊呆了!他们的编程题目简直象是抄袭的??程序结构,注释,变量命名就不说了吧,全部都是极其类似! 反观中国的牛人、高手,每个人有自己的一套。到了新的岗位,先把前任的程序贬损一通,然后自己再开发更多的问题的代码来代替。我的公 司统计,一个软件中有4个以上CSocket版本,每个人都觉得别人做得差,自己再搞一套。中国人,就是这个样子,还会辩解说“我们这样有创造 性”。其实软件发展,早就走过了求伯君那个编码英雄的年代,程序员已经是个坐办公室的蓝领了。你具备拧好一个螺丝钉的能力就可以了,Code 是最低级的事情了。
2.他们许多公司的项目经理根本就不懂技术。中国的项目经理如果不能在技术上压服下属,那么下属将与他搞鬼,越是高手越喜欢搞鬼,根本不知道作软件的终极目的是从别人兜里掏钱,而在内部搞不团结。技术高手都会纠集一些对他技术上崇拜的菜鸟,与管理层作对。 而印度的软件经理根本就不懂正在做的东西,许多甚至直接就是MBA,或者是领域专家(工业设计、地理专家等),而不是编码的专家。但是却 能够领导大群素质良好的程序员把工作做好,没有内部不团结的情况。许多印度的程序员加入一个公司很长时间,都不知道自己整天编的代码是干什么用的。给他们的任务可能就是一个函数的声明以及该函数要实现的功能。我们呢? 3.他们的编程人员的流动率达到 30%!他们的编程人员流动率(包括内部项目之间的流动)高达 30%,可以想见他们的文档水平如何。他们的 产品不依赖任何一个人,谁都可以立即辞职,产品的开发还是会正常进行。而中国,是老板怕总工。技术骨干拥兵自重,抗拒管理。任何制定好的计划,都有可能被技术人员推翻或者跟你消极怠工。 4.他们的开发计划能够做到小时级别。如果一个印度公司的项目经理没有上班,那么他的下属将可能不知道作什么。他们的计划一般都定到天,每个基层开发人员每天的工作量就是 8小时。而我们能够给出月度计划的公司就很少,而给出的月度计划要么不可能实现,要么就可能被取消。开发人员被初略的给个任务,他在月初,可以慢慢琢磨是做成什么样子,然后上上网,聊聊天。到了月中和月末,就开始熬夜编码。看到每年从各大高校不尽牛人滚滚来,我们是不得不要召人,同时又是不抱希望。我公司现在有意以后将核心软件开发外包给印度公司,中国人?做做界面吧,中国人做界面会极尽奇技淫巧,搞得花里胡哨的。BTW,大家不要误会我有什么种族歧视。但是我们现在就是对自己歧视,自卑得很。中科院那么多研究院,连个能用的操作系统都搞不定。 北大开发一些东西,比如什么青鸟CASE,就是给一帮人评职称的。杨芙清院士整天搞来搞去,搞出了什么东西?B大,T大的人最难管理,牛得看不见人。中国的程序员骂微软,追 Linux是全世界最狠的,可是我们除了汉化Linux,做了什么东西出来。CDE 是瑞典人写的,Linus是芬兰的,GNome是墨西哥人写的。哎,我们曾经是多么的瞧不起印度人......
3月21日 设计已死?(五)很难重构的东西(Things that are difficult to refactor in)
我们能用重构来处理所有设计方面的决策吗?或者,是否存在一些问题因为弥漫在整个系统中而难以在将来加入设计中?XP的正统观念是:当你需要时,任何事情都可以很容易的加入,所以YAGNI总是能够适用。我想知道如果存在意外情况呢?有一个不错的例子是软件的国际化问题。这是不是一种现在应该立即进行,否则以后再加入时会觉得痛苦的事情?
我能很容易想到一些会陷入这种境地的事情。然而事实上我们仍然了解的太少。如果你必须加入一些功能,如国际化,那么随后你就会十分清楚它要花费多少成本。但你不容易在真正应用它之前就搞清楚,周复一周的完善和维护它要花费多少成本。同样,你也不容易意识到你可能已经做错了,这样无论如何也要做些重构了。
部分能够为YAGNI辩护的理由是,许多潜在的需求到最后并不真的需要,至少不是你预料的那种方式。不过对潜在的需求不做任何事情能节省下的力气,远没有通过重构来达到实际需要花费的力气多。
另外一个要想的问题是你是否真的知道如何去做。如果你已经有做过几次软件国际化的经验,你会知道该使用什么样的模式。同样的,你更可能取得成功。如果你对将要加入的构件(structures)有一定的经验,则多半会比第一次处理这种问题效果好的多。所以我的建议是,如果你知道应该怎么去做,你就要考虑现在做和将来做,两种情形之间不同的成本。然而,如果你没有处理过类似的问题,不仅是你无法正确评估需要的成本,而且你也不太可能把事情作好。这种情形,你就要选择将来再做。如果你在将来的某一天做了,而且尝到了苦头,则你要知道这比在早期加入它的情况好的多了。当你的团队更有经验,你对相关领域有更多认识,你对需求也更了解。通常到这时你回头看才会发现事情有多简单。提早加入的设计比你想象中要难多了。
这个问题也跟素材的顺序密切相关。在Planning XP一书中,Kent和我公开的指出我们的歧见。Kent偏向于只让业务价值这一个因素影响素材的顺序。在最初的争论过后,Ron Jeffries也同意了这种想法。我仍保持怀疑。我认为应该在业务价值和技术风险之间找一个平衡点。基于这样的理由让我提前为软件国际化做准备以降低风险。但是这种做法只有当第一次释放版本就需要将软件国际化时才能成立。尽快地释放版本是非常重要的。任何在第一次释放版本中不需要的附加复杂性,值得在第一次版本释放后再做。发行之后运行的程序有强大的力量,它抓住了客户的注意力,增加了信任感并且是一个学习的好机会。要尽你一切努力来靠近第一次发行的日期。即使在初次版本释放之后添加某些功能会花费更多的成本,还是要保证提早释放版本。
与任何新技术一样,XP很普通,以至于它的拥护者都不太清楚它的使用限制条件。许多XP实践者被告知演进式设计是不可能用于解决确定的问题,不料竟发现这是完全可能的。征服了“不可能”的情况,于是认为所有这一类问题都可以解决。当然我们不能笼统的概括XP的使用界限。直到XP社团偶尔碰到了界限并且惨遭失败,我们才能确信这是XP的使用界限。而且我们有权利去尝试超越这个潜在的界限。(在Jim Shore近期的文章中讨论了一些问题,其中就包括国际化问题,这个潜在的界限最终被清除了。)
产生设计了吗?(Is Design Happening?)
演进式设计的一个难点是,很难分辨出设计是否已经发生。将设计与编码混合在一起有不去设计就编码的危险——这是造成演进式设计发散和失败的情形。 如果你在开发团队中,你就可以按照代码的质量来判断是否产生了设计。如果代码变得越来越复杂、维护越来越困难,这说明缺少足够的设计。但是遗憾的是,这只是主观观点。我们还没有一套可靠的标准来客观的衡量设计的质量。
如果缺少能见度(visibility)对于技术人员来说是困难的,那么对于团队中的非技术人员来说则是更让人担忧的。如果你是一名经理或者一名客户,你会怎么看待设计良好的软件?你应该认为是重要的,因为设计糟糕的软件将来变更的成本太昂贵了。到底什么是不好的设计,这很难回答,但是下面给出了一些提示:
1、 和技术人员交谈。如果他们抱怨做出变更非常困难,那么请重视这个问题并且给他们修改的时间。
2 、 密切注视有多少代码别遗弃掉。在重构正常的项目中,被删除的代码数量会比较稳定。如果在一段时间内没有任何代码被删除,这无疑表示没有充分的进行重构——这会造成设计退化。然而,像所有的规则一样,这个可能被滥用,因此建议信任技术高超的技术人员胜于任何规则,尽管这是主观的。
这样来看设计死了吗?(So is Design Dead?)
设计死了吗?绝对没有,只是设计的本质发生了变化。XP的设计追求以下的技巧:
l 持续保持代码干净、简单。
l 重构,使你觉得任何有必要的时候都可以大胆的改进。
l 对模式有深刻的认识:不只死搬硬套,更要知道该何时用,以及如何逐步引入。
l 着眼于应付未来变化的设计,知道现在做出的决策可能要过后进行修改。
l 知道如何将设计传达给必要的人,用代码、图表和最重要的:交谈。
以上挑出来的技巧看来都挺吓人,但是要成为一个优秀的设计师本来就很难。至少我不觉得,XP让它变得简单些。但是我想XP让我们对有效率的设计有全新的看法,因为它让演进式设计成为一种可行的方式。而且我也很支持演进——否则谁知道我在干什么呢?
致谢(Acknowledgements)
过去的这两年里,我从很多好朋友身上偷学到不少好的想法,很多都已经记不起来了。但是我记得从Joshua Kerievski那里偷到的好东西。我也记得Fred George和Ron Jeffries给我很好的建议。我当然也不能忘记来自Ward和Kent的很多好的想法。
我也感谢曾经提出问题和指出打字错误的朋友。由于我的疏忽没有保留这些朋友的列表,但是我记得有Craig Jones,Nigel Thorne,Sven Gorts,Hilary Nelson,Terry Camerlengo。
修订记录(Revision History)
以下是这篇文章重大的修改记录:
2004年5月:添加了”The Will to Design”,”Reversibility”和”Is Design Happening”三个章节。
2001年2月:对growing an architecture、the role of an architect、和where things that are difficult to add with refactoring等段落做修改。
2000年7月:原始文件在XP 2000发表,并刊载于MartinFowler.com。
[译注1]:所谓XP利用实践(exploiting practices)是建立于软件变更曲线平滑的情况下的,而XP启动实践(enabling practices)则是XP建立这个基础的方式。由于不知道国内如何来翻译者两个名词的,所以姑且这样翻译吧。
[译注2]:软件蓝图(software diagram)的翻译有点牵强,其实本意就是表达软件设计意图的图表。
[译注3]:说到隐喻,记得以前我在做一个文件管理脚本程序时,就将不同责任的脚本文件比喻成不同的狗,负责遍历目录的叫做侦探犬,负责记录的叫做档案狗…… 设计已死?(四)关于隐喻(On Metaphor)
好吧,我不妨公开的承认——我一直没有抓住隐喻(metaphor)的精神。我知道它有用,而且在C3项目中运用得很好,但是这并不表示我知道怎么用它,更不用说要解释怎么用了。
XP实践中的隐喻是建立在Ward Cunningham's为系统命名的做法上。提出一些众所周知的常用词汇,然后用这个词汇表(vocabulary)来比喻整个领域(domain)。这些代表系统特性的词汇会出现在类和方法的命名上[译注3]。
我曾经通过建造领域概念模型(conceptual model)来构造名字系统(system of names)。利用UML或者它的前身与领域专家一起建造概念模型。我发现你必须很小心的保持最精简的符号集合,而且要当心别让技术性的问题不知不觉的影响了这个模型。但是一旦你完成这个概念模型,你就可以为这个领域建立一个词汇表。这些词汇很容易理解。领域专家可以用来与开发人员沟通。概念模型无法与类设计完美的吻合,但是已足够给整个领域一个通用的词汇表。
目前我找不到任何理由说明为何这个词汇表不能运用比喻,就像C3中将工资单比喻为工厂装配线一样;我也不觉得基于领域词汇表建造你的名字系统有什么坏处。我也不会放弃我可以驾轻就熟的系统命名方式。
人们常批评XP是觉得一个系统至少需要一些笼统的设计。XP实践者们则常以“就是隐喻啊”来响应。但是我一直没有看到一个对于隐喻令人信服的解释。这是XP的空缺,需要由XP实践者们来理出头绪。
你想成为架构师吗?(Do you wanna be an Architect when you grow up?)
近几年来“软件架构师(software architect)”越来越热门,这是一个就我个人而言难以接受的术语。我太太是建筑工程师。工程师和架构师之间的关系是... 有趣的。我最喜欢的一句话是:架构师喜好三种东西:球状物、灌木丛和鸟。因为架构师画出这些美丽的图画,却要工程师保证能全都做出来。所以我避免使用软件架构师一词,毕竟如果连我的太太都不能尊重我的专业,我又怎么能对其他人有所期望呢?
在软件行业,架构一词可以代表很多含义。(软件行业中几乎所有的词都可以代表很多含义。) 概括为一句话是:我不是一名程序员——我是一名架构师。还可以进一步解译成:我现在是一名架构师——我这么重要怎么能参与编码。然后这个问题就变成了:是否一定要彻底脱离编码,才能与你的技术领导者的地位相符合? 这个问题引起众多的不满。大家一想起再也无法担任架构师这个角色就非常生气。我经常听到这样的埋怨:在XP中没有给经验丰富的架构师挥洒的空间。
就设计本身来说,我不认为XP不重视经验或者好的设计技术。事实上,我从很多XP提倡者——Kent Back、Bob Martin当然还有Ward Cunningham——那里学到了设计的理念。然而这也代表着他们的角色从大家既有的印象中开始转变成为技术领导者。
我将以一位ThoughtWorks的技术领导者Dave Rice为例。Dave曾参与过几次完整地开发,并且曾在一个50人的项目中担任非正式的技术主管。他担任主管的角色意味着要花很长的时间与程序员为伍。他会和需要帮助的程序员一起工作,否则就留意着看谁需要帮助。他的座位上有一个明显的标记。在ThoughtWorks很长一段时间,他可以适应任何形式的办公环境。他曾经与发行经理(release manager)Cara共享办公室一段时间。而在最后的几个月,他搬到了程序员们工作的开放式房间(就像XP实践者喜欢的开放式“战斗场所”)。这么做对他很重要,因为他可以知道事情的进展,并适时伸出援手。熟悉XP的人已经意识到我描述的是XP中的教练(Coach)角色。的确,在XP玩的文字游戏中将技术领导叫做教练。意义在于:在XP中技术领导者的作用是通过教导程序员和帮助他们做决定而体现出来的。这需要良好的人际关系以及高超的技术。Jack Bolles在XP2000上说:孤立的大师只会作茧自缚,合作和教导才是成功的关键。
在研讨会的晚宴上,我和Dave与一位对XP持反对观点的人谈话。当我们讨论到以前的经验,我们的方法相当的类似。我们都偏好自适应的(adaptive)和迭代式(iterative)开发,也认为测试是重要的。所以我们对他反对的立场感到疑惑。然而当他说“最后的时候我会让程序员照着设计进行重构”。事情一下子明朗起来。后来Dave又对我解释“如果他不信任他的程序员,又何必要雇用他们呢?”,观念上的隔阂就更加清楚了。在XP里头,有经验的开发人员所能做的最重要的一件事就是尽量将所有技术传递给更多的新手。你让教练来指导开发人员做出重大的决定,而不是由一个架构师来决定这一切。就像Ward Cunningham指出的那样,这么做使得他的技术得到广泛使用,这对项目的好处大于一个孤胆英雄所能做的。
可逆性(Reversibility)
在XP2002大会上,对于敏捷方法(agile methods)和精益生产(lean manufacturing)之间的关系,Enrico Zaninotto发表了一场令人陶醉的演说。按照他的观点,他认为两种方法中都有一个关键特征:它们都通过减少过程中的不可逆性(irreversibility)来降低复杂度。从这个观点来看,造成复杂度的主要因素之一是在项目中做出了不可逆的决策。如果你可以轻易的改变你做出的决策,这意味着让决策恢复正常不会对项目有太大的影响——你的生活变得简单多了。所以,在演进式设计中设计师们应该想方设法避免在设计中产生不可逆性。与其急于尝试着去做出一个正确的设计,倒不如从下面两条路中选择一条:推迟设计(直到你有了足够多的信息)或者做出一个让你在不远的将来可以很容易推翻逆转的设计。
支持可逆性的决定就是敏捷方法强调使用源代码管理系统的原因之一。虽然它不能保证可逆性,特别是对很久以前做出的决策,但是它为团队提供了一个可以信赖的基础,即使很少去使用它。可逆性设计也包含着一个可以很快发现错误的过程。迭代开发的一个重要作用就在于快速的迭代过程允许客户看着系统逐步成长,并且如果在需求中发现错误,可以很快地确定问题并做出修改,而不至于将错误堆积到让人望而却步的地步。对于设计来说快速的定位错误同样重要。这意味着你要对设计做一些装置(set things up),以便对存在潜在问题的部分做出快速的测试。这还意味着值得通过原型来模拟系统的一部分,以便试验对设计做出变更的困难度,即使你现在还不需要真的去变更。几个小组通过在原型中较早的试验变更,来评估对设计做出变更的困难度。
设计自律(The Will to Design)
我已经在这片文章中提到了很多技术实践,而人的因素则太容易被忽略了。演进式设计在工作中需要驱动它集中于一点的力量。这个力量仅仅能够来自于团队中那些能够保证高水平设计的人员。设计自律并不是来自于团队中每个人(尽管如果是这样会很好),通常在团队中只有一到两个人对全部的设计负责。这往往被认为是架构师担当的责任。
对设计负责就意味着要一直监视着代码,注意代码是否开始变得肮脏,并在局面失去控制之前采取快速的行动进行纠正。负责审查设计的人员并不一定要参与修改——但是他们一定要确保有人来修改。缺少自律的设计被认为是演进式设计失败的重要原因。即使人们对我在这片文章中提到的实践非常熟悉,没有自律的设计还是无法成功的。 设计已死?(三) 模式与XP(Patterns and XP)
JUnit的例子让我不得不想到模式。XP和模式之间的关系很微妙,也常常被问起。Joshua Kerievsky认为模式在XP被过分轻视,而且他所提出的理由也相当令人信服,我不想再重复。不过值得一提的是,对于很多人来说模式似乎与XP是有冲突的.
其实本质在于模式常常被过度的使用。世上有太多传奇的程序员,第一次读到四人帮以32行代码阐述16种模式这样的事情还记忆犹新。我还记得有一晚与Kent喝着酒一起讨论一篇叫做《不要设计模式:23种简单技巧》的文章。我们认为像文章中那样的情况使用if语句要好过策略模式。这个玩笑说明模式常常被滥用,但这并不表示模式是不足取的。问题在于你要怎样运用它。
其中一种观点是简单设计的力量自然会将设计引向模式。很多重构的例子明确地这么做了;甚至不用重构,你只要遵从简单设计的规则就会发现模式,即使你还不知道模式是什么。这种说法也许是真的,不过它真的是最好的方式吗?当然,如果你对模式有个大略的了解,或者手边有一本书可以参考,而不是自己去发明模式,那这是一种较好的方式。当我觉得快用到模式的时候,我肯定会去翻翻GOF的书。就我来说,有效的设计告诉我们,模式值得付出代价去学习——这是它特有的技能。同样地就像Joshua所建议的,我们需要更熟悉于如何逐步地运用模式。XP使用模式的方式与一般使用模式的方式有所不同,但并没有抹煞它的价值。
但是从一些邮件列表中的观点看来,我觉得很多人认为XP并不鼓励使用模式,尽管XP的大部分倡导者也都是模式运动的领导者。是因为他们已经超越了模式?或是因为他们已经将模式融入了思想而不必再去体现它?我不知道其它人的答案是什么,但是对我来说,模式仍然非常重要。XP也许可以认为是一种开发过程,而模式则是设计技术的骨干知识,不管采用哪种开发过程,这些知识都是很有用的。不同的过程使用模式的方式也许不同,XP强调直到需要时才使用模式并且通过简单的实现逐步引入模式。所以模式仍然是一种必须获得的关键知识。
我建议采用XP的人这样来使用模式:
l 花点时间学习模式。
l 留意使用模式的时机(但是别太早)。
l 先关注如何以最简单的方式使用模式,然后再慢慢增加复杂度。
l 如果你用了一种模式却觉得没有多大帮助——不用怕,再次把它去掉。
我认为使用XP应该更强调学习模式。我不确定如何让它和XP的实践合适的搭配起来,不过我相信Kent会想出办法来的。
发展架构(Growing an Architecture)
软件架构是指什么呢?对我来说,架构是系统核心元素(elements)的意思,这部分是难以改变的。剩下的都必须建造在这基础上。
那么当你使用演进式设计时,架构又扮演着什么样的角色呢?XP的批评者再一次地声称XP忽视架构,因为XP的路线是尽快地开始
写程序,相信重构会解决所有设计上的问题。有趣地是,这些批评者说得没错,这有可能是XP的缺点。的确,最激进的XP专家——
像Kent Beck,Ron Jeffries和Bob Martin——尽其所能地避免任何预先的架构性的设计。在你真的要用到数据库之前,不要加入数据库,
先用文本文件来代替,在之后的迭代中通过重构加入数据库。
我常被认为是一个胆小的XP专家,这点我不同意。我认为一个概括性的初始架构有它的用处所在。像是一开始要怎么将应用分层,如
何与数据库交互(如果你需要的话),使用哪种方式去处理web server。
实际上,我认为这些就是近年来我们所研究的模式。随着你对模式认识的加深,你会越来越习惯于在开始时就考虑如何去适当的使用它们。不过,关键性的差异在于这些初始架构的决定是可以更改的,明确地说只要团队意识到他们的判断有误时,就应该有勇气去修正它们。有人跟我讲过一个关于项目的故事,就在项目接近部署阶段时,他们突然决定不再使用EJB,并且要将已有的应用从系统中移除。这是一个规模相当大的重构,不过最后还是顺利的完成了。XP中的启动实践不仅让事情变得可能,而且很值得去做。
如果以相反的方式来做这件事呢?如果你决定不采用EJB,那么将来会很难加入吗?你是否真的要在尝试过各种方法却发现依然欠缺什么时,才使用EJB?这是一个牵涉很多因素的问题。不使用复杂的组件当然可以增加系统的简单度,而且可以让项目进展得比较快。但有时候从系统中抽掉某个部分会比加入它要容易多了。
所以我建议从评估架构可能的样子开始。如果你看到将会有多个用户操作大量的数据,那么一开始就应该直接使用数据库。如果你看到很复杂的业务逻辑,那么就套用领域模型(domain model)。当你怀疑是否偏离了简单性原则时,那就遵循YAGNI的精神。所以你要有所准备,当发现所使用的架构没有任何帮助时应尽快的简化它。
UML与XP(UML and XP)
在我投身于XP领域之后,我和UML间的关系使得一个很大的疑问一直挥之不去:这两者能兼容吗?
它们之间有一些不兼容的地方。XP显然在很大程度上不再重视画图。虽然官方的立场是“有用就用”,但是实际上却隐藏着“真正的XP实践者不需要画图”的潜台词。而且确实有很多人不习惯画图,就像Kent一样,这加强了这种观点。确实,我也从来没见过Kent主动使用固定的标记法画下软件蓝图(software diagram)[译注2]。
我觉得这个问题有两个独立的原因。第一个原因是软件蓝图对一些人来说有用,而对另一些人来说没用。有危害的是,认为软件蓝图有用的人却不是真正动手做的人,反之既然。我们应该接受并不是每个人都应该使用图表(diagram)的事实。
另一个原因是软件蓝图常倾向于引入繁重的过程(heavyweight process),这些过程耗时费力却不见得有用,甚至还会产生负面影响。我认为应该教导人们如何适当有效的使用图表并且避免落入繁重的陷阱,而不是像些XP专家说的那样“只有懦弱的人才用”。
所以,我对于适当有效的使用图表的建议是:
首先别忘了你画这些图的目的,主要的价值在于沟通。有效的沟通意味着突出重要的部分而忽略不太重要的部分。这样的取舍也是有效运用UML的关键。不必把全部的类(class)都画出来——只画出重要的就可以了;对于每个类也不必显示所有的属性(attribute)和操作(operation)——只显示重要的;也不要为所有的用例(use case)和方案(scenarios)画时序图——只... 让你了解大概的情况。在使用图表时常犯的通病就是人们通常希望详细完整的把图表现出来。其实程序代码就是提供完整信息的最佳来源,同时代码本身也是保持信息同步的最简单方式。面面俱到的图表是一目了然的敌人。
图表通常的用途是在开始编写代码之前探讨设计。在你的印象中常常觉得这样的行为在XP中是不合法的,但并不是这样的。很多人都说当你遇到棘手的问题时,是值得先将它们汇总起来开一个快速设计会议(a quick design session)。当你进行设计会议时:
l 保持简短。(keep them short)
l 不要涉及到所有的详细(只挑重要的)。
l 把结果当作是草图,而不是定案。
上面的最后一点值得深入探讨。当采用了预先式设计,随后而且往往是在编码的时候,你会不可避免的发现一些设计错误。如果你适时变更设计,它就不是问题。麻烦的是当人们认为设计已经定案时,他们不会将在编码中获得的知识反馈到设计中去。
变更设计不代表一定要更改图表。画这些图表来帮助你了解设计,然后就丢掉,这么做是非常合理的。这些图能有所帮助就有它的价值了,它们不必永远存在。最好的UML图是不会作为历史资料存在的。
不少XP实践者使用CRC卡片,这与UML并不冲突。我总是将CRC卡片和UML图混在一起,哪个更适合手头上的工作就选择哪个。
UML图的另一个用途是作为持续维护的文档资料。它一般的形式,就是在用例工具(case tool)中看到的模型。最初的想法是留着这样的资料有助于构建系统。事实上却常常没什么用。
l 保持图表更新花费太多的时间,因此常无法与代码保持同步。
l 它们被隐含在用例工具或者厚重的包装(a thick binder) 中,以致被人忽略。
所以,希望这种持续维护的文档资料起到作用,就要从这些已知的问题下手:
l 只用一些改起来不至于让人觉得痛苦的图。
l 把图放在显眼的地方。我喜欢画在墙上,鼓励大家一起动手修改。
l 注意这些图是不是有人在用,没用的就擦掉。
使用UML图的最后一种方式是作为移交工作时的文档资料,比如说在不同团队移交工作时。按照XP的观点,产品的文档就是素材(story),因此这些文档的价值已经得到了客户的肯定。于是UML又派上了用场,它所提供的图形有助于沟通。别忘了程序代码本身就蕴含了所有详细的信息,所以图形的作用只是提供概括以及突出重要的部分。 设计已死(二) 简单的价值(The value of Simplicity)
XP大声疾呼的两个口号是“考虑能够工作的最简单的事情(Do The Simplest Thing that Could Possibly Work)”和“你将不需要它(You Aren't Going to Need It)” (就是YAGNI)。它们都表达了XP提倡的实践——简单设计(simple design)。
YAGNI的意思是现在不要为了将来可能用到的功能编写任何代码。表面上好像很简单,但是问题出在像框架(framework)、可重用组件和灵活性设计上,这些东西本来就很复杂。你预先付出额外的成本去构建它们,以期望以后的开发中会节省成本。事先构建灵活的预先设计被认为是高效软件设计的关键部分。
但XP的建议是,不要以需要某项功能为理由来构建灵活的组件及框架出来。让整体架构随着需要成长。假如我今天想要一个可以处理加法但是不用乘法的Money类,我就只在Money类中构建加法功能。就算我确定下一次迭代中确实需要乘法运算,而且我知道实现会很简单。我还是会留到下一次迭代中再去做它。
其中一个理由是经济效益。如果我花时间在以后才需要的功能上,那就表示我没有将全部精力放在这次迭代中应该完成的事情上。已经发布的计划明确指明了现在要进行的工作,如果现在做将来才需要做的事情,就违背了开发人员和客户之间的协议。这种做法使得完成当前迭代过程存在风险。即使完成本次迭代所有素材不存在任何风险,要做哪些额外工作也是由客户来决定的——还是有不包括乘法功能的可能。
这种经济效益上的限制是因为我们有可能出错。就算是我们已经确定这个功能应该如何运作,都有可能出错——尤其是在我们还没有得到详细需求的时候。提前做一件错误的事情比提前做一件对的事情更浪费时间。而且XP专家们通常认为我们更有可能做错而不是做对(我心有戚戚)。
第二个支持简单设计的理由是复杂的设计要比简单的设计令人难懂。因此随着系统复杂度的提高,对系统进行任何修改变得越来越难。如此,若系统必须加入更复杂的设计时,成本势必增加。
现在很多人发现这个建议是无意义的,其实他们这样想是对的。因为你所想象的普通开发并没有采用XP启动实践。然而,当planned design和演进式设计之间的平衡有了变化时(也只有这时),YAGNI就会变成好的实践。
总之,你不要花费任何精力在以后迭代中才需要的功能上。即使实现这个功能的成本可以忽略,你也不要这样做,因为这样会增加修改成本。但是你只有在使用XP或者其它类似的降低变更成本的技术时才可以遵循以上建议。
究竟什么是简单(What on Earth is Simplicity Anyway)
因此,我们希望程序能够越简单越好,这听起来没什么好争论的,毕竟有谁想要复杂呢?但问题来了,究竟“什么是简单呢?” 在XPE一书中,Kent对简单系统制定了四个评测标准,依序是(最重要排最前面):
l 通过所有测试(Runs all the Tests)
l 呈现所有的意图(Reveals all the intention)
l 避免重复(No duplication)
l 最少数量的类或方法(Fewest number of classes or methods)
通过所有测试是一项相当简单的标准,避免重复也很明确,尽管许多开发人员需要别人的指点才能做到。比较麻烦的是“呈现所有的意图”这一项,这到底指的是什么呢?
这句话的本意就是明确的代码。XP对代码的可读性有着极高的重视。虽然在XP中,术语"clever code"已经被滥用,不过意图清楚的代码,对其他人来说是友善的(cleverness)。
Josh Kerievsky在XP 2000论文中举了一个很好的例子。他阅读了在XP领域可能是大家最熟知的JUnit的程序源码。JUnit使用装饰模式(decorators)在测试用例(test cases)中加入可选功能,像是并发同步和批量”set up”代码。将这些可选功能的代码抽取到装饰者(decorator)中,使得通用的代码更加清晰。
但是你必须扪心自问,这样做之后的程序真的简单吗?对我来说是,因为我熟悉装饰模式。但是对于不了解装饰模式的人来说还是相当复杂的。类似的,JUnit使用的可插入方法(pluggable method)对大部分刚开始接触的人来说根本不简单清晰。所以,也许我们可以说JUnit的设计对有经验的人来说是比较简单的,但是对于新手来说则比较复杂。
XP的“一次,并且仅有一次(Once and Only Once)”和《程序员修炼之道》提倡的DRY(不要重复自己)都专注在去除重复的代码。这些建议都有很显著而且惊人的效果。只要依照这个方式,你就可以避免重复。但是它不能解决所有问题,简单化还是不容易做到的。
最近我参与了一个可能被过度设计的项目。系统经过重构之后去除了部分灵活性设计。就像其中的一位开发者说的那样“重构过度设计的系统要比重构没有设计的系统容易的多”。做一个比你所需要的简单一点的设计是最好的,但是稍微复杂一点点也不是什么严重的事情。
我听过最好的建议来自Bob大叔(Robert Martin)。他的建议是不要太在意什么是最简单的设计。毕竟以后你可以,应该,也会再重构它的。最后,不断的重构,比知道怎样才能做到最简单的设计重要得多。
重构违反了YAGNI吗?(Does Refactoring Violate YAGNI?)
这个主题最近出现在XP邮件列表中,当我们审视设计在XP中扮演的角色时,我觉得很值得拿出来讨论。
基本上这个问题起因于重构需要耗费时间却没有增加新的功能。而YAGNI的观点是你应该为了眼前的需要做设计而不是未来,这样算是相互抵触吗?
YAGNI的观点是不要加入一些现阶段不需要的复杂性,这也是简单设计这条实践的部分精神。重构可以保证你的设计尽可能的简单,所以当你觉得可以让系统变得更简单的时候,就进行重构。
简单设计这条实践不仅仅是XP利用实践而且也是XP启动实践。只有基于测试、持续集成和重构才能有效的保证简单设计。而同时,简单设计又对于保持变更曲线平缓非常重要。任何不必要的复杂性都会让系统变得难于调整,除非这个调整正是你加入复杂性所预料的调整。不过,人们通常不善于预料未来,所以最好还是努力地保持简单性。同样,人们也不太可能第一次就能做到最简单,因此你需要重构来帮助你更接近这个目标。
设计已死?(一)【转载】Martin Fowler著 Ai92译
英文原文版权由Martin Fowler拥有
Original text is copyrighted by Martin Fowler Martin Fowler Chief Scientist, ThoughtWorks
声明:任何人都可以在任何地方随意转载本文,但是在转载时请保持本文完整性,请不要在转载的时候做任何改动或增删。
原文地址。
*****************************************************************
对很多粗略接触到极限编程(Extreme Programming)的人来说,XP似乎宣告了软件设计的死刑。不仅有很多的设计被嘲笑为“臃肿的预先设计(Big Up Front Design)”,就连很多设计技术,像UML、灵活的程序架构(framework),甚至连模式(pattern)都不再受到重视甚至被彻底的忽略了。事实上,XP包含了很多的设计理念,但是它与现有的软件过程有着不同的运作方式。XP通过许多实践赋予演进式设计(evolutionary design)崭新的风貌,让演进成为一种可行的设计方法(viable design strategy)。但是它也带来了新的挑战和技巧,因为设计者(designer)要去学习如何使设计精简,如何使用重构来保持设计的简洁,以及如何使用模式。(这篇文章是我在XP2000研讨会发表的演说,它会发表在研讨会讲义中。)
经过规划的设计与演进式的设计(Planned and Evolutionary Design)
XP实践(The Enabling Practices of XP)
简单的价值(The value of Simplicity)
究竟什么是简单(What on Earth is Simplicity Anyway)
重构违反了YAGNI吗?(Does Refactoring Violate YAGNI?)
模式与XP(Patterns and XP)
发展架构(Growing an Architecture)
UML与XP(UML and XP)
关于隐喻(On Metaphor)
你想成为架构师吗?(Do you wanna be an Architect when you grow up?)
可逆性(Reversibility)
设计自律(The Will to Design)
很难重构的东西(Things that are difficult to refactor in)
产生设计了吗?(Is Design Happening?)
这样来看设计死了吗?(So is Design Dead?)
致谢(Acknowledgements)
修订记录(Revision History)
XP挑战了很多在软件开发中普遍存在的行为。其中最受争议的就是反对up-front design,而支持演进的方式。批评者说这是退回到了仅有"code and fix" ,通常被嘲笑为黑客式的开发方式。而极限编程的支持者也往往被看作排斥设计技术(就像UML)、原则和模式的激进分子。不用担心什么设计,只要你倾听(listen to)你的代码,好的设计会浮现出来的。
我发现自己陷入了这个争论的中央。我从事的很多工作都和图形设计语言(graphical design languages)以及模式(patterns)有关,其中图形设计语言包括UML以及早于UML的其它图形设计语言。实际上我曾经写过关于UML和模式方面的书。我拥抱XP就意味着要放弃我曾经主张的一切,将这些“反革命”的观点统统从脑子中清除掉?
嗯... 我不想让你的思绪悬在这个疑问里。简单的说答案是否定的。接下来的文章就让我来详细说明。
经过规划的设计与演进式的设计(Planned and Evolutionary Design)
我将在这篇文章中描述软件开发中的两种设计方式。或许最常见的是演进式设计。它的本质是系统设计随着系统实现逐步增长。设计是编码过程中的一部分,并且随着代码的发展,设计也要跟着调整。
在常见的使用中,演进式设计带来的是灾难。设计的结果其实是一堆特定的战术策略(ad-hoc tactical decisions)的集合,使得代码变得更难以修改(alter)。从很多方面来看,你可能会争辩说这根本没有设计,当然会导致差劲的设计。像Kent表述的那样,所谓设计从长远的观点来看就是要做到很容易地变动软件。当设计开始变坏时,你应该能够做出有效的更改。一段时间之后,设计变得越来越糟,你就体会到了软件的混乱。这样的情形不仅使软件变得本身难以变动,也容易产生难以追踪和彻底解决的bug。随着工程的进行,修改不断出现的bug 所花费的成本呈指数地增长,这就是"code and fix"的噩梦。
Planned Design的做法正好相反,并且吸收了其它工程学中的观念。如果你打算做一间狗屋,你只需要找些木料并在心中有一个大略的构思就可以了。但是如果你想要建一栋摩天大楼,同样的做法,恐怕盖不到一半大楼就垮了。那么你要先在一间像我太太在波士顿市区那样的办公室里完成工程图。她在设计图中确定所有的细节,一部分使用数学分析,但是大部分都是使用建筑规范。所谓的建筑规范就是根据成功的经验 (有些是基础数学) 制定出的如何设计建筑的法则。一旦设计完成,她所在的设计公司就将设计图交给另一个公司按图施工。
Planned Design 将同样的方式应用在软件开发中。设计师预先设计出大部分得细节。设计师们并不负责编写代码,他们只负责设计。所以设计师们可以利用像UML这样的设计技术从编码的细节中脱离出来,而在一个比较抽象的层面上工作。一旦设计完成了,他们就可以将它交给另一个团队(甚至是另一家公司)去建造(build)。因为设计师们在宏观的层面上考虑问题,所以他们能够避免因为策略方面不断的更改而导致软件的失序。程序员遵循着设计好的方向,写出好的系统。
自从Planned design方法从七十年代出现,已经有很多人用过它了。在很多方面它比”code and fix”演进式设计要来的好。但是它也有一些缺点。第一个缺点是当你在编写代码时,你不可能把所有必须处理的问题都想清楚。所以将无可避免的遇到一些让人对原先设计产生质疑的问题。可是如果设计师在完成工作之后就转移到了其它项目,怎么办?程序员开始迁就设计来写程序,于是软件开始趋于混乱。就算找到了设计师,花时间整理设计,变更设计图,然后修改程序代码。但是必须面临更短促的时间压力来修改问题,又是混乱的开端。
此外,Planned design通常还有文化方面的问题。设计师有着熟练的技巧和丰富的经验。然而,他们忙于从事设计而没有时间来编写代码。可是,软件开发使用的工具和素材(materials)发生着日新月异的变化。当你不再编写代码时,你不仅仅错失了技术变革带来的好处,同时也失去了那些编写代码的程序员对你的尊敬。
建造者(builder)和设计者之间这种紧张的关系在建筑行业也看得到,只是在软件行业更加凸显而已。在软件行业之所以会如此强烈是因为和建筑行业有着一个关键性的差异。在建筑行业,设计师和工程师的技术有清楚的分别;但是在软件行业就分不太清楚了。任何在高度注重设计的环境中工作的程序员都必须具备熟练的技术,以便足够对设计师的设计提出质疑,尤其是当设计师对于新的开发工具或平台越来越不熟悉的情况下。
这些问题也许可以获得解决。也许我们可以解决人与人之间的紧张状态。也许我们可以加强设计师的技术来处理绝大部分的问题,并且制订出一个修改设计图的过程准则。但是仍然有另外一个问题:变更需求。变更需求是软件项目中最让我感到头痛的问题之一。
处理变更需求的方式之一是做灵活的设计,以便当需求有所变动时,你就可以轻易的变更设计。然而,这需要你对将要发生的变动有深刻的洞察力。一项预留处理易变性质的设计可能对于可预知的需求变更有所帮助,但是对于无法预知的需求变更却没有帮助(甚至有害)。所以你必须对需求有足够的了解以隔离易变的部分,照我观察,这是非常困难的。
部分有关需求的问题被归咎于对需求的了解不够清楚。所以很多人开始专注于研究需求工程过程(requirements engineering processes),希望得到更准确的需求以避免后面对设计的修改。但是即使朝这个方向去做一样无法解决问题。很多无法预知的需求变更起因于业务的变化。这是不能避免的,即使你小心翼翼的使用着需求工程过程。
以上问题使得planned design听起来像是不可能的实现的。无疑,这是很有挑战性的。但是我不认为planned design比使用”code and fix”方式的演进式设计差。甚至,我更喜欢 planned design。因为我了解 planned design 的缺点,并且正在寻找更好的方法。
XP实践(The Enabling Practices of XP)
XP因为许多原因而备受争议,其中最关键的原因就是它主张演进式设计而不是planned design。正如我们知道的,演进式设计可能因为特定的设计策略(ad hoc design decisions)和照成软件开发混乱而行不通。
理解这个争议的关键在于软件变更曲线(software change curve)。变更曲线说明,随着项目的进行,变更所花费的成本呈现指数的增长。变更曲线按照不同的阶段可以表示为:在分析阶段花一块钱所作的变更,放在编码阶段中则要花费数千元。讽刺的是大部分的工程仍然没有分析过程而以非正式的方式进行着,但是这种成本上的指数关系还是存在着。变更曲线意味着演进式设计可能行不通,它同时也说明了为什么planned design要小心翼翼的进行,因为在planned design中产生的任何错误同样会导致指数增长。
XP的基础是建立在将变更曲线拉平,使得演进式设计可行的假设上的。XP将变更曲线拉平而又为自己所用。就XP中一系列相互依赖的实践来说:你不能仅仅使用那部分以平滑曲线为前提的XP利用实践(exploiting practices)而放弃那些建造平滑曲线这个前提的XP启动实践(enabling practices) [译注1]。这就是争论的来源,因为很多人不了解这其间的关系。通常评论家们提出的批评,是基于自身对XP的体验,但是他们割裂了XP中相互依赖的实践,结果可想而知,于是对XP的印象也就这样了。
XP中有许多启动实践,其中最重要的是测试(Testing)和持续集成(Continuous Integration)。如果没有测试提供保障,其它的XP实践都将变得不可行。持续集成可以保持团队成员信息同步,这样当你修改代码时,不必担心与其他团队成员集成会有问题。协同运用这些实践可以很大的影响变更曲线(change curve)。这让我再次想起在ThoughtWorks引入测试和持续集成之后,明显的减轻了开发工作量(development effort)。这的确令人质疑是不是必须像XP所主张的那样,要用到所有的实践才能得到大幅改善。
重构(Refactoring)具有类似的作用。那些采用XP建议的原则对程序代码进行重构的人发现,这么做要比无章法或是特殊方式的restructuring明显更有效率。这也曾是Kent指导我进行适当的重构得到的难忘经验。也因为这一次巨大的转变促使我以重构为主题写了一本书。
Jim Highsmith写了一篇很棒的文章《XP概要(summary of XP)》,文中他把planned design和重构模拟作了天秤的两端。假设你不会随后变更需求,那么在大多数传统方法中planned design占有优势。但是当需求发生变更,则重构占有了优势,它可以大幅降低变更带来的成本。当然我们不能完全抛弃planned design,而是采用一种保持“天平”平衡的协作方式。就我个人理解是:我会一次做完所有的设计然后不断的重构。
持续集成、测试和重构这些启动实践让演进式设计变得可行起来。然而我们尚未找到其间的平衡点。我相信,不论外界对XP存有什么样的印象,XP不仅仅是测试、编码和重构。在编码之前还是需要进行设计。一些设计要在所有的编码开始之前进行,而大部分的设计直到迭代中要实现具体功能时才进行。但是预先设计和重构之间要找到新的平衡。 3月20日 《深入浅出设计模式》翻译练习——The Decorator PatternThe Decorator PatternDesign Principle 5Classes should be open for extension, but closed for modification. 类应当对“扩展”开放,对“更改”封闭。 Our goal is to allow classes to be easily extended to incorporate new behavior without modifying existing code. What do we get if we accomplish this? Designs that are resilient to change and flexible enough to take on new functionality to meet changing requirements. 我们的目标是让类能够很容易地扩展来吸收新的行为,而不需要改动现有的代码。我们该做些什么来达到这一目的呢?那就是设计要能弹性改变并且足够灵活,这样才能对面向变化需求的新功能做出反应。
Now it’s time to compute the cost for the customer. We do this by calling cost() on the outermost decorator, Whip, and Whip is going to delegate computing the cost to the objects it decorates. Once it gets a cost, it will add on the cost of the Whip.
The Decorator Pattern Attaches additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionally. 装饰者模式动态地附加额外的责任给一个对象。装饰者为子类扩展功能提供了一个灵活的选择。 就好像喝茶。我上课时告诉Paul我在杭州喝龙井茶时都要用刚煮开的水,要不是龙井茶没有味儿。而Paul告诉我英国人喝茶时,会泡很浓的茶,往里面加奶、加糖、加盐、加柠檬片以及其它莫名其妙的东西,美国人甚至在茶里面加冰块!这要是茶馆把分号开到欧美去,这结算系统(ordering system)得疯掉——茶里加的每一样东西都要额外算钱,并且可以任意组合。会不会有人往茶里面加蓝莓呢?希望洋鬼子不要把几种茶叶加到一起喝——龙井加铁观音加毛尖? 还没看出来问题吗?单纯的龙井茶就已经是一种饮料(a kind of soft drink)了,随着洋鬼子抽风一样往龙井里面加配料(加奶、加糖、加盐、加柠檬片,还有最傻的加冰),“龙井茶”三个字已经不能准确描述你现在的饮料了,那你该怎么算账呢?不同的洋鬼子抽不同的风就会加不同的配料,就会配出来不同的“龙井茶”,可是咱们茶馆的结算系统只认识“龙井茶”,怎么办(你要是来个“加奶的龙井茶”、“加糖的龙井茶”,“加冰的龙井茶”……恭喜你,你体会到什么叫‘类爆炸——class explosion’了)? “弄个容器把配料都扔进去,算钱的时候一遍历加起来就行了!” “那个谁,你给我闭嘴,坐下!” 其实解决的办法就是只对“一样东西”算钱——任一种饮料被加入配料时,自动被“装饰”成配料,就是说系统只看见加到龙井里的柠檬片,而看不见龙井了!结算时“柠檬片”知道自个儿该给系统什么数——“Call the cost() method and rely on delegation to add on the condiment costs”。 再简单点儿,杯子里盛满水,问你这时杯子有多重?当然是水杯的重量加上水的重量了!我只要问你“杯子的重量”就行了,傻子才问你“这时杯子和水有多重”——地球人没这么说话的! “水”在加上“杯子”后,就只剩下“杯子”了。如果再把“杯子”放到“载物箱”中,那么杨利伟在发射前问你“那个谁,这次载物箱有多重?”时,你就该知道怎么回答了。 好了,那个谁,我知道如果只是算账的话,配料扔到容器里再遍历求和是可行的,但是如果不是饮料而是在装配中各种除成本结算外有各自行为的物料呢?如果是生产的不同工序中,每个工序除了成本核算外又都有各自行为呢?如果是神州七号,每节助推器除了提供推力外又都有各自独特的轨道姿态调整使命呢?那可就不是简单地从容器里取东西,再加加减减的事了,每样“配料”的行为都要影响自身和整体——装配件和基体,工序和被加工物件,助推器和神州七号飞船。 把茶馆换成星巴克,龙井茶换成咖啡也是一样一样一样的! 不放鱼的鱼香肉丝依然是鱼香肉丝——叫不叫鱼香肉丝不取决于你放了什么。 While that describes the role of the Decorator Pattern, it doesn’t give us a lot of insight into how we’d apply the pattern to our own implementation. Let’s take a look at the class diagram, which is a little more revealing (on the next page we’ll look at the same structure applied to the beverage problem). 在描述装饰者模式的规则时,并没有在如何应用该模式到我们的实现软件上给我们多少深刻的理解。我们来看一眼类图,它能有多点启发。 Patterns Exposed This week’s interview: Confessions of a Decorator 模式大暴露(很黄很暴力) 本周采访:一名装饰者的内心坦白 HeadFirst: Welcome Decorator Pattern. We’ve heard that you’ve been a bit down on yourself lately? Decorator: Yes, I know the world see me as the glamorous design pattern, but you know, I’ve got my share of problems just like everyone. HF:欢迎装饰者模式。我们听说你最近有点低落? 装饰者:是的,我知道全世界都把我看成是个迷人的设计模式,但是你知道,我也和每个人一样要分担些问题。
HeadFirst: Can you perhaps share some of your troubles with us? Decorator: Sure. Well, you know I’ve got the power to add flexibility to designs, that much is for sure, but I also have a dark side. You see, I can sometimes add a lot of small classes to a design and this occasionally results in a design that’s less than straightforward for others to understand. HF:你能让我们也分担一些你的麻烦吗? 装饰者:当然。哦,你明白的我具有为设计添加灵活性的威力,这确实是真的,但我也有“阴暗面”。你看,我有时想设计中加入了许多小类,这偶尔会在设计中造成与简单易懂的设计 不易理解。
HeadFirst: Can you give us an example? Decorator: Take the Java I/O libraries. These are notoriously difficult for people to understand at first. But if they saw the classes as a set of wrappers around an InputStream, life would be much easier. HF:能给我们个例子吗? 装饰者:拿Java的I/O类库举例。最初这个类库的难理解是臭名昭著的。但是只要人们只要把那些难理解的类看成是围绕InputStream的一系列封装,生活就简单多了。
HeadFirst: That doesn’t sound so bad. You’re still a great pattern, and improving this is just a matter of public education, right? Decorator: There’s more, I’m afraid. I’ve got typing problems: you see, people sometimes take a piece of client code that relies on specific types and introduce decorators without thinking though everything. Now, one great think about me is that you can usually insert decorators transparently and the client never has to know it’s dealing with a decorator. But like I said, some code is dependent on specific types and when you start introducing decorators, boom! Bad things happen. HF:那东西听起来没那么糟。你仍然是个了不起的模式,要改变这一情况那只是公众教育的事,不是你的责任,对吗? 装饰者:恐怕远不止这些。我还有类型的问题:你知道,人们有时候弄出来一段依赖特殊类型的代码,然后在没想明白每件事之前就引入了装饰者。现在,我考虑最多的就是你可以透明地插入多个装饰者,这样客户就根本不知道他要和装饰者打交道。但是就像我所说的,有些代码是依赖于特定类型的,当你开始引入装饰者时,轰!出问题了。
HeadFirst: Well, I think everyone understands that you have to be careful when inserting decorators, I don’t think this is reason t be too down on yourself. Decorator: I know, I try not to be. I also have the problem that introducing decorators can increase the complexity of the code needed to instantiate the component. Once you’ve got decorators, you’ve got to not only instantiate the component, but also wrap it with who knows how many decorators. HF:好了,我想每个人都明白在引入装饰者时必须要仔细些,我不认为这能成为你情绪低落的原因。 装饰者:我明白,我也试着不这样。我还有引入装饰者会增加实例化组件的代码复杂度这一问题。一旦你使用了装饰者,你就不能仅仅是要实例化一个组件,还要实例化封装它的装饰者,天知道会有多少装饰者。
HeadFirst: I’ll be interviewing the Factory and Builder patterns next week – I hear they can be very helpful with this. Decorator: That’s true; I should talk to those guys more often. HF:下周我要采访工厂和建筑者模式——我听说他们这两种模式对你的情况很有帮助。 装饰者:是这样;看来我得多和这两个人聊聊。
HeadFirst: Well, we all think you’re a great pattern for creating flexible designs and staying true to the Open-Closed Principle, so keep your chin up and think positively! Decorator: I’ll do my best, thank you. HF:好了,我们都认为你是一个了不起的模式,能用来创建灵活的设计,能坚持Open-Closed原则,所以keep your chin up,还有想的积极些! 装饰者:我会尽力的,谢谢你。
BULLET POINTS 3Inheritance is one form of extension, but not necessarily the best way to achieve flexibility in our designs. 继承是一种扩展的形式,但还不是能达到在你的设计中满足灵活性的最好形式。 In our designs we should allow behavior to be extended without the need to modify existing code. 在我们的设计中,我们应该允许行为能够在不修改现有代码的情况下被扩展。 Composition and delegation can often be used add new behaviors at runtime. 组合及代理通常被用来在运行时添加新的行为。 The Decorator Pattern provides an alternative to subclassing for extending behavior. 装饰者模式提供了一种继承子类来扩展行为的选择。 Decorator classes mirror the type of the components they decorate. (In face, they are the same type they decorate, either through inheritance or interface implementation.) 装饰者类反应了他们装饰的组件的类型。(实际上,他们和所装饰的类型是一样的,不管是通过继承还是接口实现都是这样。) Decorator change the behavior of their components by adding new functionality before and/or after (or even in place of) method calls to the component. 装饰者通过在组件方法的“前后左右”添加方法来改变组件的行为。 You can wrap a component with any number of decorators. 你可以用任意数量的装饰者来封装一个组件。 Decorators are typically transparent to the client of the components; that is, unless the client is relying on the component’s concrete type. 装饰者对客户的组件来说是典型的透明——当然,得在客户不依赖于具体的组件类型的情况下。 Decorators can result in many small objects in our design, and overuse can be complex. 装饰者会在我们的设计中产生许多小对象,所以过度使用会加大复杂性。 3月19日 《深入浅出设计模式》翻译练习——The Observer PatternThe Observer PatternDefines a one-to-many dependency between objects so that when one object changes state, all of its dependents are notified and updated automatically. 在对象之间定义一种一对多依赖关系,使得“一”对象改变状态时,所有依赖它的“多”对象都能被通知到并自动更新。
Design Principle 4Strive for loosely coupled designs between objects that interact. 尽力在需要交互的对象间实现松散连接设计(低耦合)。 Loosely coupled designs allow us to build flexible OO systems that can handle change because they minimize the interdependency between objects. 松散连接设计使得建造灵活的面向对象系统成为可能,这样的系统能够处理变动,因为这些系统将对象之间的依存关系最小化。
Fireside Chats 炉边谈话 Tonight’s talk: A Subject and Observer spar over the right way to get state information to the Observer. 今晚的谈话是:主体(【哲】(意识的)主体;主观意识)和观察者就获取状态信息的正确方式展开争论。 Subject I’m glad we’re finally getting a chance to chat in person. Observer Really? I thought you didn’t care much about us Observer. 主体: 我很高兴我们终于有机会聊聊天。 观察者: 真的吗?我还以为你不怎么关心我们观察者呢。 Subject Well, I do my job , don’t I? I always tell you what’s going on… Just because I don’t know who you are doesn’t mean I don’t care. And besides, I do know the most important thing about you---you implement the Observer interface. Observer Well yeah, but that’s just a small part of who I am. Anyway, I know a lot more about you. 主体: 好吧,我只是做我的工作,难道不是吗?我一直告诉你正在发生什么……就是说我不知道你是谁并不意味着我不关心你。况且,我确实知道你最重要的事情——你实现了观察者接口。 观察者: 哦,但是知道我是谁仅仅是一小部分。某种程度上,我对你更了解一些。 Subject Oh yeah, like what? Observer Well, you’re always passing your state around to us Observers so we can see what’s going on inside you. Which gets a little annoying at times… 主体: 哦,比如说? 观察者: 比如说,你总是把你的状态发送给我们,这样我们就知道你这里正在发生什么事。有时候这有点让人讨厌…… Subject Well excuse me. I have to send my state with my notifications so all you lazy Observers will know what happened! Observer Ok, wait just a minute here; first, we’re not lazy, we just have other stuff to do in between your oh-so-important notifications, Mr. Subject, and second, why don’t you let us come to you for the state we want rather than pushing it out to just everyone? 主体: 哦,我打断一下。我不得不用通知来发送我的状态,这样你们这些懒汉观察员才能知道发生了什么! 观察者: 好,在这儿打住。首先,我们不是懒汉,我们只是有其它事情要忙,哦,在你重要的通知之间,主体先生。其次,你为什么不让我们到你那里自己找想要的状态,反而要自己“推”给我们每一个人? Subject Well… I guess that might work. I’d have to open myself up even more though to let all you Observers come in and get the state that you need. That might be kind of dangerous. I can’t let you come in and just snoop around looking at everything I’ve got. Observer Why don’t you just write some public getter methods that will let us pull out the state we need? 主体: 哦……我猜那或许可行。我得让自己更开放些,能让你们观察员进到内部找你们需要的状态信息。那可能有些危险。我不能让你们进到内部四处窥探我拥有东西。 观察者: 哎呀,那你就写几个让我们能“拽”出我们要的状态信息的公共getter方法不就得了? Subject Yes, I could let you pull my state. But won’t that be less convenient for you? If you have to come to me every time you want something, you might have to make multiple method calls to get all the state you want. That’s why I like push better…then you have everything you need in one notification. Observer Don’t be so pushy! There’s so many kinds of us Observers, there’s no way you can anticipate everything we need. Just let us come to you to get the state we need. That way, if some of us only need a little bit of state, we aren’t forced to get it all. It also makes things easier to modify later. Say, for example, you expand yourself and add some more state, well if you use pull, you don’t have to go around and change the update calls on every observers, you just need to change yourself to allow more getter methods to access out additional state. 主体: 对呀,我可以让你们“拽”出我的状态信息。但是这样对你是不是会不方便?如果每次你想要什么东西时都得来我这,你就得用一堆方法调用来得到所有你想要的信息。这也是我为什么喜欢“推”——这样一个通知你就能获得所有信息。 观察者: 大哥,别这么冲动好不好!地球上有那么多种观察员,你不可能预料到我们需要的东西。你就让我们到你那儿自己找需要的状态信息吧。这样的话,如果我们中有人只需要一小部分状态信息,我们就不用非得获取全部状态信息。这样还让以后的更改更简单。比如说,你扩展了自己还加了些新的状态信息,如果你用“拽”的话,你就不用跑一圈去更改每一个观察员更新状态信息的方法,你就改一下你自己,加几个接收新加的状态信息的getter方法就行了。 Subject Well, I can see the advantages to doing it both ways. I have noticed that there is a build-in Java Observer Pattern that allows you to use either push or pull. Observer Oh really? I think we’re going to look at that next… 主体: 好了,我明白这两种方式各自的优点了。我注意到有一个能让你使用“推”或“拽”的内建的Java观察员模式能。 观察者: 真的吗?我想我们得去看看…… Subject Great…maybe I’ll get to see a good example of pull and change my mind. Observer What, us agree on something? I guess there’s always hope. 主体: 很好……看样我得找个“拽”的好范例来看看,来变变思路。 观察者: 什么,咱俩这不想一块去了吗?我就觉得肯定又希望。 BULLET POINTS 2The Observer Pattern defines a one-to-many relationship between objects. 观察者模式在对象之间定义了一种“一对多”的关系。 Subjects, or as we also know them, Observable, update Observers using a common interface. 主体,或者说是我们知道的,被观察对象(翻译的不好),用一个共同的接口来更新观察员。 Observers are loosely coupled in that the Observable knows nothing about them, other than that they implement the Observer Interface. 观察员是松散关联的(低耦合),即是说被观察对象除了知道观察员实现了观察者接口以外,对他们一无所知。 You can push or pull date from the Observable when using the pattern (pull is considered more “correct”). 使用观察者模式时,你可以从被观察对象那里“被推”或“拽”来数据信息(‘拽’更‘正确’点儿)。 Don’t depend on a specific order of notification for you Observers. 对观察员来说,不要依赖于特定的通知命令。 Java has several implementations of the Observer Pattern, including the general purpose java.util.Observable. Java有许多观察者模式的实现组件,包括一般用途的java.util.Observable包。 Watch out for issues with the java.util.Observable implementation. 小心对待实现java.util.Observable包的东西。 Don’t be afraid to create your own Observable implementation if needed. 如果形势需要,别害怕建立你自己的被观察对象。 Swing makes heavy use of the Observer Pattern, as do many GUI frameworks. 正如许多GUI框架所做的,Swing大量使用了观察者模式。 You’ll also find the pattern in many other places, including JavaBeans and RMI. 你可以在许多其它地方发现观察者模式,包括在JavaBeans and RMI里面。 3月18日 《深入浅出设计模式》翻译练习——An IntroductionAn Introduction(包含Strategy模式)Design Principle 1Identify the aspects of your application that vary and separate them from what stays the same. 发现你的业务中呈多样化的部分,把它们从稳定的业务中隔离出来。 “vary”想了好多种翻译,“变化”、“变动”、“更改”……都不大准确,都不符合“接口”存在的目的。是的,“多样化”!如果不是为了实现多样化,“面向过程”也不会功成身退。当然还有“重用”,也就是“reuse”,其实正是因为软件所模拟的真实世界具有无穷尽的“多样化”,软件才需要“重用”,笨得要实现每一种“多样化”并且要处理每一种“多样化”及其“多样化组合”所产生的行为的软件,就是一堆废物,收破烂儿的都不要!
Design Principle 2Program to an interface, not an implementation. 针对接口编程,而不是实现。 我还记的在定制班上《Hibernate》时任长宁老师批评我:“不要歧视接口!”我向毛主席保证,我从没歧视接口,那时我还不了解接口,了解后就开始歧视继承了…… I find myself a totally different new world once I understand interface. Design Principle 3Favor composition over inheritance. 青睐组成超过继承。 看看,说着说着就开始歧视继承了。
As you’ve seen, creating systems using composition gives you a lot more flexibility. Not only does it let you encapsulate a family of algorithms into their own set of classes, but it also lets you change behavior at runtime as long as the object you’re composing with implements the correct behavior interface. 正如你看到的,使用组合来创建系统赋予你更多的灵活性。不仅仅是封装一整套算法到类的集合里,更厉害的是在运行时改变行为(藉此来实现多态),只要你用来参与组合的行为对象实现了正确的接口。 根据本章中的例子,在模拟器模拟不同的鸭子时,鸭子共同的行为“飞”、“叫”会自动改变,甚至同一只鸭子可以实现不同的“飞”,只要你用另一种“飞”来组合出这只鸭子,整个过程的代码简单程度远远超过你的想象,以至于比你正在读的这句话还要短! 最要命的是,更改某一种“飞”时(比如说让野鸭的“飞”从3000米飞‘一’字队形,变成2000米飞‘人’字队形),完全不会干扰其它种类的“飞”!天哪,那个“痴人说梦”就这么实现了!你只要简单的打开“野鸭飞.cs”文件(请理解我使用C#),修改“飞”这个方法并编译就行了。根本不会影响什么“红头鸭”的“飞”、“Mallard鸭”的“飞”、“唐老鸭”的“飞”…… Defines a family of algorithms, encapsulates each one, and makes them interchangeable. Strategy lets the algorithm vary independently form clients that use it.
定义了一整套算法,封装每一种算法,并让它们可以互换。Strategy使得算法能在使用它的客户群中独立地改变形式。
BULLET POINTS 1Knowing the OO basics does not make you a good OO designer. 了解面向对象的基础知识并不会让你成为一名令人满意的面向对象设计师。 Good OO designs are reusable, extensible and maintainable. 令人满意的面向对象设计是可重用、可扩展以及可维护。 我就听过公司的项目经理这样抱怨过:“代码已经乱套了,一个jsp能有好几百行,无数人写过这个页面,到最后没人看得懂,改一个地方有时候就全乱套了!所以我要求他们……” 看,代码的不可维护性出来了,修改一个地方结果对其它功能造成了未知的影响,哑巴治成聋子!最可怕的是,这种影响是不可预测的,出了问题大家才发现。 Patterns are proven object-oriented experience. 模式是被检验过的面向对象经验。 Patterns don’t give you code, they give you general solutions to design problems. You apply them by your specific application. 模式没有给你代码,它们给你的是对设计问题的普遍性解决方案。你要在自己的程序中应用它们。 Patterns aren’t invented, they are discovered. 模式不是被创造出来的,而是被发现的。 实事求是的解决问题,而不是投机取巧。你只要认真了解业务就会发现问题应该怎么解决或者为什么客户仍没解决。 客户是怎么处理问题的?大家经常忽视这一问题。总觉得自己智力超群,刚看到问题就说:“这个简单,我就给你个窗口让你能改一下这个单子不就行了吗?”根本没有将问题放在整个系统的背景下考虑。销售人员将票据合并、拆解、撤销、转签,通常意味着随后的运输、签单、验收、结算、财务走账都会有相应的特殊处理,而这些都是潜伏在水下的。一旦开启了区别于标准作业流程的特殊作业流程的源头,就势必意味着你的软件要处理随后出现的流程环节,因为客户已经使用了开头,又怎么可能放弃结尾呢? 我猜中了开头,但是没猜中这结尾!——《大话西游》 The incident should be viewed against the wider context of the whole of business. 软件的不可维护性此时才要发威。在水下的噩梦浮出水面时才发现前面提供给了客户一个设计有缺陷的开头,这个缺陷可能已经和其它功能深深地交缠在了一起,可能已经成为系统重要的、不可或缺的状态,可能已经和其它业务产生了组合…… 历经加班,通常是通宵加班,而且是冠以“不计代价”、“必须完成”、“无论如何”、“辛苦辛苦”的加班后,大家长吁一声:“终于完成了!”可事实却是,噩梦才仅仅开了个头…… The end is just the beginning. 没错,软件的不可维护性此时才要发威!当然这个牵扯到调研(需求获取)阶段的问题,但是设计和需求获取正是一个硬币的正反面,必然联系在一起! 再说说那些冠冕堂皇的词:“不计代价”、“必须完成”、“无论如何”,如果没有充分的准备——战场勘察、情报搜集、火力对比,只是一味的不考虑代价、牺牲,那这个指挥员就是在送手下去死!好吧,我没有在说《集结号》。趟上这种上司是每一个属下的不幸! 人生三大幸——好父母,好老师,好领导。我们只能在后两者上做些选择。 而且,上司在客户面前痛诉自己的队伍如何通宵加班、如何放弃休假、如何辛苦才完成客户的特殊业务,通常只会让客户在心里嘲笑:“如此简单的问题都处理成这样,我们的老系统干这个可是小菜一碟,不害臊!”这哪里是什么夸耀的资本,这种事只能在心里憋着,回公司后向上司吐口水! 回到开头,客户为什么要这样处理特殊流程或为什么仍处理不了特殊流程,是大家要深入思考的。有时候思考的范围在业务内,有时候又会超出业务范畴。“无法处理”、“处理不好”对客户来说都只能归结到一个——不愿意处理,有隐情!一个行业的人为了革命事业献了青春献终身,献了终身献子孙,怎么会在数十年中想不出如何处理特殊流程呢?不要以为只有自己是IQ过120的! Most Patterns and principles address issues of change in software 大多数模式和原则都是写给软件中的“变化”问题的。 翻译的不好。好了,我想作者已经回答了“设计模式为什么会存在?” Most patterns allow some part of a system to vary independently of all other parts. 大多数模式使系统中的一部分能相对于其它部分独立变化成为可能。 这个部分回答了“什么时候使用设计模式?” We often try to take what varies in a system and encapsulate it. 我们经常试着找出系统中的变化并封装它。 Patterns provide a shared language that can maximize the value of you communication with other developers. 模式提供了一种供分享的语言来将你和其他开发人员交流沟通的价值最大化。 《深入浅出设计模式》翻译练习前言设计模式为什么会存在? 什么时候使用设计模式? 设计模式适合中国软件业吗? 设计模式好用吗?
将《深入浅出设计模式》作为这段时间英语学习的练习题,课下翻译重点,课上请教难点,尽力解答上面的问题。我已经退出IT行业,不带有任何说教及争论目的。 Table of contents (summary) 1 Welcome to Design Patterns: an introduction 2 Keeping your Objects in the know: the Observer Pattern 观察者模式 3 Decorating Objects: the Decorator Pattern 装饰者模式 4 Baking with OO goodness: the Factory Pattern 工厂模式,好多人理解的工厂模式都是错的…… 5 One of a Kind Objects: the Singleton Pattern独身模式,我更喜欢“光棍模式” 6 Encapsulating Invocation: the Command Pattern 命令模式,我更喜欢“指挥部模式” 7 Being Adaptive: the Adapter and Facade Patterns 外观模式 8 Encapsulating Algorithms: the Template Method Pattern 模板模式,我更喜欢“工序模式” 9 Well-managed Collections: the Iterator and Composite Patterns 迭代模式和组合模式 10 The State of Things: the State Pattern 状态模式 11 Controlling Object Access: the Proxy Pattern 代理模式 12 Patterns of Patterns: Compound Patterns 复合模式,“复合”翻译得不好 13 Patterns in the Real World: Better Living with Patterns 和模式幸福地生活 14 Appendix: Leftover Patterns 漏网的模式
Wouldn’t it be dreamy if only there were a way to build software so that when we need to change it, we could do so with the least possible impact on the existing code? We could spend less time reworking code and more making the program do cooler things… 需要改动时,我们能以对现有代码最小的冲击来完成,以这样的方式来建造软件,那不会是痴人说梦吧?我们花更少的时间返工同时让程序完成更cool的任务…… 这一定不是痴人说梦,如果是的话,软件行业就不是个暴利的行业。 This must not be dreamy, if not so, the software industry couldn’t be a profiteering industry. 这一定是痴人说梦,如果不是的话,为什么我的同僚们加班如此之严重? This must be dreamy, if not, why my colleagues have to work overtime so seriously? 3月15日 记住这20句型,学英语没那么难
1. I'd like to invite you to my home. 2. Thank you very much for helping me. 3. Could you please speak slowly? 4. I'm happy to help you anytime. 5.How was your vacation? 6. What do you think of our company? 7.Do you have any advice for me about learning English? 8.I'd be happy to show you around Beijing. 9.Is it possible for you to come tomorrow? 10. I wish I could help you. 11. Don't worry about being laughed at when you speak English. 12. It's too hot to eat outside. 13. Please come as quickly as you can. 14. Do you mind my opening the window? 15. I'm sure that you will have a good time here. 16. It's very important to have good manners. 17. Let me know if you visit Beijing. 18. How is your new job going? 19. It's really great that you speak two languages. 20.The best way to learn English is to practice speaking it every day. 3月10日 新上海滩——有多少人这样糊弄上级!话说许文强落难香港,冯劲尧派了个杀手去香港斩草除根,结果几天后杀手回来上海报告,说把许文强杀了。。
事隔一年,冯在舞会上突然发现仿佛有许的踪影,马上找来那个杀手问清楚,导致了以下的对话,从此之后冯一蹶不振。
冯: “许文强到底杀了没有?” 杀手: “冯老板您这么英名,领导这么有方,许文强能不死吗?“ 冯:“我问你。你到底有没有亲手杀他?“ 杀手:“我何止亲手杀他,我还和他周旋了整整一天呢“ 冯:“许文强这么英雄,你一天就能搞定?你到底是怎样杀他的?“ 杀手:“我可是费了很大的功夫的呀,许文强容易杀吗?我踹开门,枪声四起呀“ 冯:“你中埋伏了?“ 杀手:“枪是我在开呀,他拼命喊“不要,不要”,我哪能心软,啪啪就搞定了“ 冯:“许文强死了?“ 杀手:“全家人都死了,何止许文强?“ 冯:“许文强胆子这么小,还喊”不要,不要“?“ 杀手:“许文强不在,那是他老婆,不过他老婆,孩子,岳父,全给我杀死了“ 冯:“你不是说全家人都死了吗?“ 杀手:“全家人是死了呀“ 冯:“那许文强呢?“ 杀手:“许文强也死了呀“ 冯:“你不是刚说他不在吗?“ 杀手:“他当时是不在家呀“ 冯:“他不在家,你跟我说干吗?我管他家里人!#¥%×!“ 杀手:“我不是要跟您仔细汇报吗?“ 冯:“那许文强到底有没有死?“ 杀手:“死了,后来追出去了,掉下山崖了“ 冯:“你亲眼看到他掉下去的?” 杀手:“衣服都留在山边了“ 冯:“我问你有没有亲眼看到他掉下去?“ 杀手:“那肯定是他的衣服呀,我还确认了好几遍呢“ 冯:“#¥%×!” 冯:“那你有没有看到尸体呀?“ 杀手:“那当然有啦,我怎么可能没有认尸呢?“ 冯:“你在哪里找到尸体的?“ 杀手:“我跟你说,山崖下面是海,是不可能找到尸体的呀“ 冯:“#¥%×!你不是是说看到尸体了吗?找不到,又怎么能看到呢?“ 杀手:“诶,尸体是会浮出水面的呀“ 冯:“你看着尸体浮出来?“ 杀手:“那是要花好几天的,当天是看不到的” 冯:“#¥%×!啊?他不是马上浮出来,你怎么知道那个是许文强呢?” 杀手:“这个是没办法百分百确定的呀!” 冯:“#¥%×!” 3月7日 什么是食品安全? 今天算是知道什么叫食品安全了。
以前总是在电视里面听“食品安全”、“农药残留”、“绿色食品”、“纸馅包子”(虽然是假新闻)、“有毒饺子”……其实你作为一个普通的食品消费者,你根本不可能看懂各种食品标签上的指标数据、奇形怪状的符号,只能自我安慰的认为生产厂家贴这些标签时良心未泯、工商公仆们检查时铁面无私。
是不是感觉很无奈,是不是感觉要挑选“安全”的食品无从下手,没有什么感性的指标?
看看老美的公司采购部是怎么要求提供蓝莓的——什么一堆一堆的技术标准我就不说了——每公斤单冻蓝莓的梗(长度不得超过5mm)数量不得超过2个!
这种标准又是怎么实行的呢?各种检测机器轮翻过堂后,最后一到检测工序是100来号人一颗一颗的用手检查!
你说,就这样一堆蓝莓呈现在你眼前的时候,你还用得着去看什么食品标签上的指标数据、奇形怪状的符号吗?你只需要看价格就行了!价格当然是相当得不一般!可是那是对中国人来说,美国人有钱,吃个蓝莓不算什么!记得《越狱》里面有一次林肯回忆他照顾LJ吗?林肯给LJ做的就是蓝莓饼(林肯说的),林肯当时的经济状况不用我多说吧。看看资本主义就是这么的剥削我们的社会主义!至于为什么,请看这里。
总之,食品安全绝对是个感性的东西,也应该是个感性的东西——看得见、摸得着、感觉得到,而不应该是一堆莫名其妙的数据和标签。 不会因战场的改变而改变 离职后好多人一遇见我就问我有什么打算,谢谢大家的关心,现陈述如下:
2008年3月10日——2008年7月19日,我在大连外国语学院强化英语口语和听力。学习完后会找一个外派到欧美的工作,估计工作个1——2年,熟悉一下当地的环境及政策。随后就在当地开设一个办事处,公司(自家开的公司)直接将货物发给我,我和客户直接接触,同时也可以开拓市场。
个人计划陈述完毕。如果一切按计划实现,我稳定后就把该接的人接出去,体验一下万恶的资本主义剥削!
在稳定之后,把该接的人接出去之前,本人承接各种夹带、走私、假结婚业务,有需要者请留言!哈哈哈
同时请散布在各大洲的小学、中学、大学同学注意,请做好接待工作! 3月6日 大漠苍狼 这篇小说正在连载中,确实好看(原文)。
下面是节选:
“在写下这一切之前,我考虑了很久,因为很多东西,并不是三言两语就可以说的清楚,有的,到了现在我都不清楚到底是怎么回事情,更有很多东西,不符合当时的世界观,本身就不应该流传后世。 而我最后之所以决定记述下来,是因为我感觉这样的故事,如果我不说出来,实在是一个遗憾,也是对某些人的不负责。 我是一个地质勘探队员,曾经隶属于解放军地质勘探工程连,在那个红色疯狂的岁月中,我们幸运又不幸的游离于革命风暴之外,穿行于中国的大山河川之中,寻找那深埋在地底的财富。在长达10年的勘探生活中,我们穿过了中国80%的无人区域,经历了极端的枯燥与艰苦,也遇到过许多匪夷所思,惊骇莫名的事情。而这些事情,你永远也不可能在档案资料中看到,那都是一些“不应该存在的”事实,被永远的封存起来了。”
……
“裴青小心翼翼的打起手电,顺着那人的脚照上去,一照就更惊讶,这个人穿着和我们一样的解放军军装,连武装带都是一样的,手电再往上照,就看到他衣服上全是血,脸部给安全帽遮着,看不清楚,但是显然也全是血。我的脸色就绿了,立马想到这人是谁了,一边就听到王四川也骂了句蒙古话,一个战士叫了出来,“是钟胡子!钟胡子没死!”说着就要放下枪跑过去。 “别过去!”副班长喝斥了一声,眼睛都充血了。“你看他那样子!看清楚了!”我们都明白副班长的意思,如果真的是钟胡子没有死,看到我们早就打招呼了,怎么会在哪里一动不动,好比一具僵尸一样看着我们,到现在都没反应。”
……
“由此人的身份,我们马上就意识到,早于我们的那一只探险队的规格之高,已经超过了我们的想象,如果要再高一点,恐怕只剩下李四光,黄岌青那帮人了。” |
|
|