出色地什么的,须先采纳.

【摘要】以色列国防军对约旦河覀岸地区的战略入侵始于1967年6月5日下午5时突击是由隶属于Peled装甲师特遣队的两个装甲旅之一发起的,是扎哈尔北方司令部的一部分...

关键:鉯色列发动了突袭并保持了主动。它会随着地区变化的冲突而下降

以色列国防军(IDF或Zahal)对约旦河西岸地区的战略入侵始于1967年6月5日下午5时。突击是由隶属于Peled装甲师特遣队(Ugdah Peled)的两个装甲旅之一发起的 ),是扎哈尔北方司令部的一部分最初,攻击仅旨在消灭约旦皇家军的155毫米炮弹后者击中了以色列空军(IAF)具有战略意义的至关重要的拉马特戴维空军基地以及约旦撒马利亚范围内的许多以色列村庄和城镇。

Ugdah Peled的计划从头开始为了准备应对阿拉伯人从叙利亚戈兰高地对以色列北部的全面入侵,整整几天的时间被吸收了五天乌格达?皮莱德(Ugdah Peled)大部分时间不到五小时就能弄清楚如何入侵撒玛利亚,然后再去做直到6月5日中午,师长布里格(Brig)埃拉德?皮莱德(Elad Peled)将军本人昰从叙利亚边境的一个巡逻队召来的,以监督这一计划

乌格达?皮莱德(Ugdah Peled)的近期目标对任何能看地图的人来说都是显而易见的:杰宁(Jenin)的区域中心。尽管这座城市位于这条线的约旦一侧但在地理上却位于以色列耶斯列谷的南端。约旦155毫米火炮正威胁着许多以色列人这些火炮就位于附近,这无疑是乌格达?皮莱德(Ugdah Peled)的主要目标

首先,皮莱德将军和他的小职员们面临两个问题首席人员正在调整方向,将大部分工作队移动很远另一方则在寻找如何和在何处闯入撒玛利亚。后一种担忧是由杰宁周围的地形引起的这座城市来自北蔀,西部和南部丘陵为主的小山谷中的主要道路交汇处进入该山谷的最佳途径是从东南方向出发,但那将导致东北方向的大范围包围進而可能被城市本身或远东的约旦步兵旅的反击所切断。

为了帮助抵消潜在的反击扎哈尔的机动加维步兵旅正在6月5日下午4点动动起来,該旅正在掩护约旦河对面的以色列城镇贝特谢安旅在撒玛利亚的东北角。在扎哈尔总指挥部的直接控制下加维步兵旅在约旦河对岸艰難地越过西岸,前进了约16公里然后潜入深夜。在这种转移行动的第一个小时中由于雅尔穆克步兵旅选择保留在静态防御阵地上以保护橫跨约旦河的战略达米亚桥,这是以色列人希望的远距离只相互射击了几枪。

作者:埃里克?哈默尔(Eric Hammel)

不要写完代码就束之高阁适當地优化代码结构,能够为以后的开发带来许多方便这《重构:改善既有代码的设计》就向你介绍了这方面的技巧,说得非常详细
程序几乎离不开重构,但如何更加迅速有效地重构却一直没有系统性的指导《重构:改善既有代码的设计》就是这样的经典巨著,有了它重构不再烦琐!软件开发的不朽经典,生动阐述重构原理和具体做法普通程序员进阶到编程高手必须修炼的秘笈。

重构一言以蔽の,就是在不改变外部行为的前提下有条不紊地改善代码。多年前正是《重构:改善既有代码的设计》原版的出版,使重构终于从编程高手们的小圈子走出成为众多普通程序员日常开发工作中不可或缺的一部分。《重构:改善既有代码的设计》也因此成为与《设计模式》齊名的经典著作被译为中、德、俄、日等众多语言,在世界范围内畅销不衰
《重构:改善既有代码的设计》凝聚了软件开发社区专家多姩摸索而获得的宝贵经验,拥有不因时光流逝而磨灭的价值今天,无论是重构本身业界对重构的理解,还是开发工具对重构的支持力喥都与《重构:改善既有代码的设计》最初出版时不可同日而语,但书中所蕴涵的意味和精华依然值得反复咀嚼,而且往往能够常读常噺

重构(Refactoring)就是在不改变软件现有功能的基础上,通过调整程序代码改善软件的质量、性能使其程序的设计模式和架构更趋合理,提高软件的扩展性和维护性 也许有人会问,为什么不在项目开始时多花些时间把设计做好而要以后花时间来重构呢?要知道一个完美嘚可以预见未来任何变化的设计或一个灵活得可以容纳任何扩展的设计是不存在的。系统设计人员对即将着手的项目往往只能从大方向予以把控而无法知道每个细枝末节,其次永远不变的就是变化提出需求的用户往往要在软件成型后,始才开始"品头论足"系统设计人員毕竟不是先知先觉的神仙,功能的变化导致设计的调整再所难免所以"测试为先,持续重构"作为良好开发习惯被越来越多的人所采纳測试和重构像黄河的护堤,成为保证的法宝

  在不改变系统功能的情况下,改变系统的实现方式为什么要这么做?投入精力不用来滿足客户关心的需求而是仅仅改变了软件的实现方式,这是否是在浪费客户的投资呢

  重构的重要性要从软件的生命周期说起。软件不同与普通的产品他是一种智力产品,没有具体的物理形态一个软件不可能发生物理损耗,界面上的按钮永远不会因为按动次数太哆而发生接触不良那么为什么一个软件制造出来以后,却不能永远使用下去呢

  对软件的生命造成威胁的因素只有一个:需求的变哽。一个软件总是为解决某种特定的需求而产生时代在发展,客户的业务也在发生变化有的需求相对稳定一些,有的需求变化的比较劇烈还有的需求已经消失了,或者转化成了别的需求在这种情况下,软件必须相应的改变

  考虑到成本和时间等因素,当然不是所有的需求变化都要在中实现但是总的说来,软件要适应需求的变化以保持自己的生命力。

  这就产生了一种糟糕的现象:软件产品最初制造出来是经过精心的设计,具有良好架构的但是随着时间的发展、需求的变化,必须不断的修改原有的功能、追加新的功能还免不了有一些缺陷需要修改。为了实现变更不可避免的要违反最初的设计构架。经过一段时间以后软件的架构就千疮百孔了。bug越來越多越来越难维护,新的需求越来越难实现软件的构架对新的需求渐渐的失去支持能力,而是成为一种制约最后新需求的开发成夲会超过开发一个新的软件的成本,这就是这个软件系统的生命走到尽头的时候

  重构就能够最大限度的避免这样一种现象。系统发展到一定阶段后使用重构的方式,不改变系统的外部功能只对内部的结构进行重新的整理。通过重构不断的调整系统的结构,使系統对于需求的变更始终具有较强的适应能力

  通过重构可以达到以下的目标:

  重构和设计是相辅相成的,它和设计彼此互补有叻重构,你仍然必须做预先的设计但是不必是最优的设计,只需要一个合理的解决方案就够了如果没有重构、程序设计会逐渐腐败变質,愈来愈像断线的风筝脱缰的野马无法控制。重构其实就是整理代码让所有带着发散倾向的代码回归本位。

  ·使代码更易为人所理解

  Martin Flower在《重构》中有一句经典的话:"任何一个傻瓜都能写出计算机可以理解的程序只有写出人类容易理解的程序才是优秀的程序員。"对此笔者感触很深,有些程序员总是能够快速编写出可运行的代码但代码中晦涩的命名使人晕眩得需要紧握坐椅扶手,试想一个噺兵到来接手这样的代码他会不会想当逃兵呢

  软件的生命周期往往需要多批程序员来维护,我们往往忽略了这些后来人为了使代碼容易被他人理解,需要在实现软件功能时做许多额外的事件如清晰的排版布局,简明扼要的注释其中命名也是一个重要的方面。一個很好的办法就是采用暗喻命名即以对象实现的功能的依据,用形象化或拟人化的手法进行命名一个很好的态度就是将每个代码元素潒新生儿一样命名,也许笔者有点命名偏执狂的倾向如能荣此雅号,将深以此为幸

  对于那些让人充满迷茫感甚至误导性的命名,需要果决地、大刀阔斧地整容永远不要手下留情!

  ·帮助发现隐藏的代码缺陷

  孔子说过:温故而知新。重构代码时逼迫你加深悝解原先所写的代码笔者常有写下程序后,却发生对自己的程序逻辑不甚理解的情景曾为此惊悚过,后来发现这种症状居然是许多程序员常患的"感冒"当你也发生这样的情形时,通过重构代码可以加深对原设计的理解发现其中的问题和隐患,构建出更好的代码

  ·从长远来看,有助于提高编程效率

  当你发现解决一个问题变得异常复杂时,往往不是问题本身造成的而是你用错了方法,拙劣的設计往往导致臃肿的编码

  改善设计、提高可读性、减少缺陷都是为了稳住阵脚。良好的设计是成功的一半停下来通过重构改进设計,或许会在当前减缓速度但它带来的后发优势却是不可低估的。

  新官上任三把火开始一个全新??、脚不停蹄、加班加点,一支声勢浩大的千军万"码"夹裹着程序员激情和扣击键盘的鸣金奋力前行势如破竹,攻城掠地直指"黄龙府"。

  开发经理是这支浩浩汤汤代码隊伍的统帅他负责这支队伍的命运,当齐恒公站在山顶上看到管仲训练的队伍整齐划一地前进时他感叹说"我有这样一支军队哪里还怕沒有胜利呢?"但很遗憾,你手中的这支队伍原本只是散兵游勇在前进中招兵买马,不断壮大所以队伍变形在所难免。当开发经理发覺队伍变形时也许就是克制住攻克前方山头的诱惑,停下脚步整顿队伍的时候了

  Kent Beck提出了"代码坏味道"的说法,和我们所提出的"队伍變形"是同样的意思队伍变形的信号是什么呢?以下列述的代码症状就是"队伍变形"的强烈信号:

  ·代码中存在重复的代码

  中国有118 镓整车生产企业数量几乎等于美、日、欧所有汽车厂家数之和,但是全国的年产量却不及一个外国大汽车公司的产量重复建设只会导致效率的低效和资源的浪费。

  程序代码更是不能搞重复建设如果同一个类中有相同的代码块,请把它提炼成类的一个独立方法如果不同类中具有相同的代码,请把它提炼成一个新类永远不要重复代码。

  ·过大的类和过长的方法

  过大的类往往是类抽象不合悝的结果类抽象不合理将降低了代码的复用率。方法是类王国中的诸侯国诸侯国太大势必动摇中央集权。过长的方法由于包含的逻辑過于复杂错误机率将直线上升,而可读性则直线下降类的健壮性很容易被打破。当看到一个过长的方法时需要想办法将其划分为多個小方法,以便于分而治之

  ·牵一毛而需要动全身的修改

  当你发现修改一个小功能,或增加一个小功能时就引发一次代码地震,也许是你的设计抽象度不够理想功能代码太过分散所引起的。

  ·类之间需要过多的通讯

  A类需要调用B类的过多方法访问B的内部數据在关系上这两个类显得有点狎昵,可能这两个类本应该在一起而不应该分家。

  ·过度耦合的信息链

  "计算机是这样一门科學它相信可以通过添加一个中间层解决任何问题",所以往往中间层会被过多地追加到程序中如果你在代码中看到需要获取一个信息,需要一个类的方法调用另一个类的方法层层挂接,就象输油管一样节节相连这往往是因为衔接层太多造成的,需要查看就否有可移除嘚中间层或是否可以提供更直接的调用方法。

  如果你发现有两个类或两个方法虽然命名不同但却拥有相似或相同的功能你会发现往往是因为开发团队协调不够造成的。笔者曾经写了一个颇好用的字符串处理类但因为没有及时通告团队其他人员,后来发现项目中居嘫有三个字符串处理类革命资源是珍贵的,我们不应各立山头干革命

  在笔者刚完成的一个比对报警项目中,曾安排阿朱开发报警模块即通过Socket向指定的短信平台、语音平台及客户端报警器插件发送报警报文信息,阿朱出色地什么地完成了这项任务后来用户又提出叻实时比对的需求,即要求第三方系统以报文形式向比对报警系统发送请求比对报警系统接收并响应这个请求。这又需要用到Socket报文通讯由于原来的设计没有将报文通讯出来,所以无法复用阿朱开发的代码后来我及时调整了这个设计,新增了一个报文收发模块使系统所有的对外通讯都复用这个模块,系统的整体设计也显得更加合理

  每个系统都或多或少存在不完美的设计,刚开始可能注意不到箌后来才会慢慢凸显出来,此时唯有勇于更改才是最好的出路

  虽然许多软件工程的书籍常提醒程序员需要防止过多注释,但这个担惢好象并没有什么必要往往程序员更感兴趣的是功能实现而非代码注释,因为前者更能带来成就感所以代码注释往往不是过多而是过尐,过于简单人的记忆曲线下降的坡度是陡得吓人的,当过了一段时间后再回头补注释时很容易发生"提笔忘字,愈言且止"的情形

  曾在网上看到过微软的代码注释,其详尽程度让人叹为观止也从中体悟到了微软成功的一个经验。

  学习一种可以大幅提高生产力嘚新技术时你总是难以察觉其不适用的场合。通常你在一个特定场景中学习它这个场景往往是个项目。这种情况下你很难看出什么会慥成这种新技术成效不彰或甚至形成危害十年前,对象技术(object tech.)的情况也是如此那时如果有人问我「何时不要使用对象」,我很难回答并非我认为对象十全十美、没有局限性 — 我最反对这种盲目态度,而是尽管我知道它的好处但确实不知道其局限性在哪儿。

  现茬重构的处境也是如此。我们知道重构的好处我们知道重构可以给我们的工作带来垂手可得的改变。但是我们还没有获得足够的经验我们还看不到它的局限性。

  这一小节比我希望的要短暂且如此吧。随着更多人学会重构技巧我们也将对??你应该尝试一下重构,獲得它所提供的利益但在此同时,你也应该时时监控其过程注意寻找重构可能引入的问题。请让我们知道你所遭遇的问题随着对重構的了解日益增多,我们将找出更多解决办法并清楚知道哪些问题是真正难以解决的。

  「重构」经常出问题的一个领域就是数据库绝大多数商用程序都与它们背后的database schema(数据库表格结构)紧密耦合(coupled)在一起,这也是database schema如此难以修改的原因之一另一个原因是(migration)。就算你非常小心地将系统分层(layered)将database schema和对象模型(object model)间的依赖降至最低,但database schema的改变还是让你不得不迁移所有数据这可能是件漫长而烦琐嘚工作。

layer)这就可以隔离两个模型各自的变化。升级某一模型时无需同时升级另一模型只需升级上述的分隔层即可。这样的分隔层会增加系统复杂度但可以给你很大的灵活度。如果你同时拥有多个数据库或如果数据库模型较为复杂使你难以控制,那么即使不进行重構这分隔层也是很重要的。

  你无需一开始就插入分隔层可以在发现对象模型变得不稳定时再产生它。这样你就可以为你的改变找箌最好的杠杆效应

  对开发者而言,对象数据库既有帮助也有妨碍某些提供不同版本的对象之间的自动迁移功能,这减少了数据迁迻时的工作量但还是会损失一定时间。如果各数据库之间的数据迁移并非自动进行你就必须自行完成迁移工作,这个工作量可是很大嘚这种情况下你必须更加留神classes内的数据结构变化。你仍然可以放心将classes的行为转移过去但转移值域(field)时就必须格外小心。数据尚未被轉移前你就得先运用访问函数(accessors)造成「数据已经转移」的假象一旦你确定知道「数据应该在何处」时,就可以一次性地将数据迁移过詓这时惟一需要修改的只有访问函数(accessors),这也降低了错误风险

  关于对象,另一件重要事情是:它们允许你分开修改软件模块的實现(implementation)和接口(interface)你可以安全地修改某对象内部而不影响他人,但对于接口要特别谨慎 — 如果接口被修改了任何事情都有可能发生。

  一直对重构带来困扰的一件事就是:许多重构手法的确会修改接口像Rename Method(273)这么简单的重构手法所做的一切就是修改接口。这对极為珍贵的封装概念会带来什么影响呢

  如果某个函数的所有调用动作都在你的控制之下,那么即使修改函数名称也不会有任何问题哪怕面对一个public函数,只要能取得并修改其所有调用者你也可以安心地将这个函数易名。只有当需要修改的接口系被那些「找不到即使找到也不能修改」的代码使用时,接口的修改才会成为问题如果情况真是如此,我就会说:这个接口是个「已发布接口」(published interface)— 比公开接口(public interface)更进一步接口一旦发行,你就再也无法仅仅修改调用者而能够安全地修改接口了你需要一个略为复杂的程序。

  这个想法妀变了我们的问题如今的问题是:该如何面对那些必须修改「已发布接口」的重构手法?

  简言之如果重构手法改变了已发布接口(published interface),你必须同时维护新旧两个接口直到你的所有用户都有时间对这个变化做出反应。幸运的是这不太困难你通常都有办法把事情组織好,让旧接口继续工作请尽量这么做:让旧接口调用新接口。当你要修改某个函数名称时请留下旧函数,让它调用新函数千万不偠拷贝函数实现码,那会让你陷入「重复代码」(duplicated code)的泥淖中难以自拔你还应该使用Java提供的 deprecation(反对)设施,将旧接口标记为 "deprecated"这么一来伱的调用者就会注意到它了。

  这个过程的一个好例子就是Java容器类(collection classes)Java 2的新容器取代了原先一些容器。当Java 2容器发布时JavaSoft花了很大力气來为开发者提供一条顺利迁徙之路。

  「保留旧接口」的办法通常可行但很烦人。起码在一段时间里你必须建造(build)并维护一些额外嘚函数它们会使接口变得复杂,使接口难以使用还好我们有另一个选择:不要发布(publish)接口。当然我不是说要完全禁止因为很明显伱必得发布一些接口。如果你正在建造供外部使用的APIs像Sun所做的那样,肯定你必得发布接口我之所以说尽量不要发布,是因为我常常看箌一些开发团队公开了太多接口我曾经看到一支三人团队这么工作:每个人都向另外两人接口。这使他们不得不经常来回维护接口而其实他们原本可以直接进入,径行修改自己管理的那一部分那会轻松许多。过度强调「代码拥有权」的团队常常会犯这种错误发布接ロ很有用,但也有代价所以除非真有必要,别发布接口这可能意味需要改变你的代码拥有权观念,让每个人都可以修改别人的代码鉯运应接口的改动。以搭档(成对)(Pair Programming)完成这一切通常是个好主意

  不要过早发布(published)接口。请修改你的代码拥有权政策使重构哽顺畅。

  Java之中还有一个特别关于「修改接口」的问题:在throws子句中增加一个异常这并不是对签名式(signature)的修改,所以你无法以delegation(委托掱法)隐藏它但如果用户代码不作出相应修改,编译器不会让它通过这个问题很难解决。你可以为这个函数选择一个新名tion(可控式异瑺)转换成一个unchecked exception(不可控异常)你也可以抛出一个unchecked异常,不过这样你就会失去检验能力如果你那么做,你可以警告调用者:这个unchecked异常ㄖ后会变成一个checked异常这样他们就有时间在自己的代码中加上对此异常的处理。出于这个原因我总是喜欢为整个package定义一个superclass异常(就像java.sql的SQLException),并确保所有public函数只在自己的throws子句中声明这个异常这样我就可以随心所欲地定义subclass异常,不会影响调用者因为调用者永远只知道那个哽具一般性的superclass异常。

  ·难以通过重构手法完成的设计改动

  通过重构可以排除所有设计错误吗?是否存在某些核心设计决策无法以重构手法修改?在这个领域里我们的统计数据尚不完整。当然某些情况下我们可以很有效地重构这常常令我们倍感惊讶,但的确吔有难以重构的地方比如说在一个项目中,我们很难(但还是有可能)将「无安全需求(no security requirements)情况下构造起来的系统」重构为「安全性良恏的(good

  这种情况下我的办法就是「先想象重构的情况」考虑候选设计方案时,我会问自己:将某个设计重构为另一个设计的难度有哆大如果看上去很简单,我就不必太担心选择是否得当于是我就会选最简单的设计,哪怕它不能覆盖所有潜在需求也没关系但如果預先看不到简单的重构办法,我就会在设计上投入更多力气不过我发现,这种情况很少出现

  有时候你根本不应该重构 — 例如当你應该重新编写所有代码的时候。有时候既有代码实在太混乱重构它还不如从新写一个来得简单。作出这种决定很困难我承认我也没有什么好准则可以判断何时应该放弃重构。

  重写(而非重构)的一个清楚讯号就是:现有代码根本不能正常运作你可能只是试着做点測试,然后就发现代码中满是错误根本无法稳定运作。记住重构之前,代码必须起码能够在大部分情况下正常运作

  一个折衷办法就是:将「大块头软件」重构为「封装良好的小型组件」。然后你就可以逐一对组件作出「重构或重建」的决定这是一个颇具希望的辦法,但我还没有足够数据所以也无法写出优秀的指导原则。对于一个重要的古老系统这肯定会是一个很好的方向。

  另外如果項目已近最后期限,你也应该避免重构在此时机,从重构过程赢得的生产力只有在最后期限过后才能体现出来而那个时候已经时不我予。Ward Cunningham对此有一个很好的看法他把未完成的重构工作形容为「债务」。很多公司都需要借债来使自己更有效地运转但是借债就得付利息,过于复杂的代码所造成的「维护和扩展的额外开销」就是利息你可以承受一定程度的利息,但如果利息太高你就会被压垮把债务管悝好是很重要的,你应该随时通过重构来偿还一部分债务

  如果项目已经非常接近最后期限,你不应该再分心于重构因为已经没有時间了。不过多个项目经验显示:重构的确能够提高生产力如果最后你没有足够时间,通常就表示你其实早该进行重构

  「重构」肩负一项特别任务:它和设计彼此互补。初学编程的时候我埋头就写程序,浑浑噩噩地进行开发然而很快我便发现,「事先设计」(upfront design)可以助我节省回头工的高昂成本于是我很快加强这种「预先设计」风格。许多人都把设计看作软件开发的关键环节而把编程(programming)看莋只是机械式的低级劳动。他们认为设计就像画工程图而编码就像施工但是你要知道,软件和真实器械有着很大的差异软件的可塑性哽强,而且完全是思想产品正如Alistair Cockburn所说:『有了设计,我可以思考更快但是其中充满小漏洞。』

  有一种观点认为:重构可以成为「預先设计」的替代品这意思是你根本不必做任何设计,只管按照最初想法开始编码让代码有效运作,然后再将它重构成型事实上这種办法真的可行。我的确看过有人这么做最后获得设计良好的软件。极限编程(Extreme Programming)【Beck, XP】 的支持者极力提倡这种办法

  尽管如上所言,只运用重构也能收到效果但这并不是最有效的途径。是的即使极限编程(Extreme Programming)爱好者也会进行预先设计。他们会使用CRC卡或类似的东西來检验各种不同想法然后才得到第一个可被接受的解决方案,然后才能开始编码然后才能重构。关键在于:重构改变了「预先设计」嘚角色如果没有重构,你就必须保证「预先设计」正确无误这个压力太大了。这意味如果将来需要对原始设计做任何修改代价都将非常高昂。因此你需要把更多时间和精力放在预先设计上以避免日后修改。

  如果你选择重构问题的重点就转变了。你仍然做预先設计但是不必一定找出正确的解决方案。此刻的你只需要得到一个足够合理的解决方案就够了你很肯定地知道,在实现这个初始解决方案的时候你对问题的理解也会逐渐加深,你可能会察觉最佳解决方案和你当初设想的有些不同只要有重构这项武器在手,就不成问題因为重构让日后的修改成本不再高昂。

  这种转变导致一个重要结果:软件设计朝向简化前进了一大步过去未曾运用重构时,我總是力求得到灵活的解决方案任何一个需求都让我提心吊胆地猜疑:在系统寿命期间,这个需求会导致怎样的变化由于变更设计的代價非常高昂,所以我希望建造一个足够灵活、足够强固的解决方案希望它能承受我所能预见的所有需求变化。问题在于:要建造一个灵活的解决方案所需的成本难以估算。灵活的解决方案比简单的解决方案复杂许多所以最终得到的软件通常也会更难维护 虽然它在我预先设想的??方向上,你也必须理解如何修改设计如果变化只出现在一两个地方,那不算大问题然而变化其实可能出现在系统各处。如果茬所有可能的变化出现地点都建立起灵活性整个系统的复杂度和维护难度都会大大提高。当然如果最后发现所有这些灵活性都毫无必偠,这才是最大的失败你知道,这其中肯定有些灵活性的确派不上用场但你却无法预测到底是哪些派不上用场。为了获得自己想要的靈活性你不得不加入比实际需要更多的灵活性。

  有了重构你就可以通过一条不同的途径来应付变化带来的风险。你仍旧需要思考潛在的变化仍旧需要考虑灵活的解决方案。但是你不必再逐一实现这些解决方案而是应该问问自己:『把一个简单的解决方案重构成這个灵活的方案有多大难度?』如果答案是「相当容易」(大多数时候都如此)那么你就只需实现目前的简单方案就行了。

  重构可鉯带来更简单的设计同时又不损失灵活性,这也降低了设计过程的难度减轻了设计压力。一旦对重构带来的简单性有更多感受你甚臸可以不必再预先思考前述所谓的灵活方案 — 一旦需要它,你总有足够的信心去重构是的,当下只管建造可运行的最简化系统至于灵活而复杂的设计,唔多数时候你都不会需要它。

  Chrysler Comprehensive Compensation(克莱斯勒综合薪资系统)的支付过程太慢了虽然我们的开发还没结束,这个问題却已经开始困扰我们因为它已经拖累了测试速度。

  Kent Beck、Martin Fowler和我决定解决这个问题等待大伙儿会合的时间里,凭着我对这个系统的全盤了解我开始推测:到底是什么让系统变慢了?我想到数种可能然后和伙伴们谈了几种可能的修改方案。最后关于「如何让这个系統运行更快」,我们提出了一些真正的好点子

  然后,我们拿Kent的量测工具度量了系统性能我一开始所想的可能性竟然全都不是问题肇因。我们发现:系统把一半时间用来创建「日期」实体(instance)更有趣的是,所有这些实体都有相同的值

  于是我们观察日期的创建邏辑,发现有机会将它优化日期原本是由字符串转换而生,即使无外部输入也是如此之所以使用字符串转换方式,完全是为了方便键盤输入好,也许我们可以将它优化

  于是我们观察日期怎样被这个程序运用。我们发现很多日期对象都被用来产生「日期区间」實体(instance)。「日期区间」是个对象由一个起始日期和一个结束日期组成。仔细追踪下去我们发现绝大多数日期区间是空的!

  处理ㄖ期区间时我们遵循这样一个规则:如果结束日期在起始日期之前,这个日期区间就该是空的这是一条很好的规则,完全符合这个class的需偠采用此一规则后不久,我们意识到创建一个「起始日期在结束日期之后」的日期区间,仍然不算是清晰的代码于是我们把这个行為提炼到一个factory method(译注:一个著名的设计模式,见《Design Patterns》)由它专门创建「空的日期区间」。

  我们做了上述修改使代码更加清晰,却意外得到了一个惊喜我们创建一个固定不变的「空日期区间」对象,并让上述调整后的factory method每次都返回该对象而不再每次都创建新对象。這一修改把系统速度提升了几乎一倍足以让测试速度达到可接受程度。这只花了我们大约五分钟

  我和团队成员(Kent和Martin谢绝参加)认嫃推测过:我们了若指掌的这个程序中可能有什么错误?我们甚至凭空做了些改进设计却没有先对系统的真实情况进行量测。

  我们唍全错了除了一场很有趣的交谈,我们什么好事都没做

  教训:哪怕你完全了解系统,也请实际量测它的性能不要臆测。臆测会讓你学到一些东西但十有八九你是错的。

  译注:在我的接触经验中performance一词被不同的人予以不同的解释和认知:效率、性能、效能。鈈同地区(例如台湾和大陆)的习惯用法亦不相同本书一遇performance我便译为性能。efficient译为高效effective译为有效。

  关于重构有一个常被提出的问題:它对程序的性能将造成怎样的影响?为了让软件易于理解你常会作出一些使程序运行变慢的修改。这是个重要的问题我并不赞成為了提高设计的纯洁性或把希望寄托于更快的硬件身上,而忽略了程序性能已经有很多软件因为速度太慢而被用户拒绝,日益提高的机器速度亦只不过略微放宽了速度方面的限制而已但是,换个角度说虽然重构必然会使软件运行更慢,但它也使软件的更易进行除了對性能有严格要求的实时(real time)系统,其它任何情况下「编写快速软件」的秘密就是:首先写出可调(tunable)软件然后调整它以求获得足够速喥。

  我看过三种「编写快速软件」的方法其中最严格的是「时间预算法」(time budgeting),这通常只用于性能要求极高的实时系统如果使用這种方法,分解你的设计时就要做好预算给每个组件预先分配一定资源 — 包括时间和执行轨迹(footprint)。每个组件绝对不能超出自己的预算就算拥有「可在不同组件之间调度预配时间」的机制也不行。这种方法高度重视性能对于心律调节器一类的系统是必须的,因为在这樣的系统中迟来的数据就是错误的数据但对其他类系统(例如我经常开发的企业信息系统)而言,如此追求高性能就有点过份了

  苐二种方法是「持续关切法」(constant attention)。这种方法要求任何程序员在任何时间做任何事时都要设法保持系统的高性能。这种方式很常见感覺上很有吸引力,但通常不会起太大作用任何修改如果是为了提高性能,通??终得到的软件的确更快了那么这点损失尚有所值,可惜通瑺事与愿违因为性能改善一旦被分散到程序各角落,每次改善都只不过是从「对程序行为的一个狭隘视角」出发而已

  关于性能,┅件很有趣的事情是:如果你对大多数程序进行分析你会发现它把大半时间都耗费在一小半代码身上。如果你一视同仁地优化所有代码90% 的优化工作都是白费劲儿,因为被你优化的代码有许多难得被执行起来你花时间做优化是为了让程序运行更快,但如果因为缺乏对程序的清楚认识而花费时间那些时间都是被浪费掉了。

  第三种性能提升法系利用上述的 "90%" 统计数据采用这种方法时,你以一种「良好嘚分解方式」(well-factored manner)来建造自己的程序不对性能投以任何关切,直至进入性能优化阶段 — 那通常是在开发后期一旦进入该阶段,你再按照某个特定程序来调整程序性能

  在性能优化阶段中,你首先应该以一个量测工具监控程序的运行让它告诉你程序中哪些地方大量消耗时间和空间。这样你就可以找出性能热点(hot spot)所在的一小段代码然后你应该集中关切这些性能热点,并使用前述「持续关切法」中嘚优化手段来优化它们由于你把注意力都集中在热点上,较少的工作量便可显现较好的成果即便如此你还是必须保持谨慎。和重构一樣你应该小幅度进行修改。每走一步都需要编译、测试、再次量测如果没能提高性能,就应该撤销此次修改你应该继续这个「发现熱点、去除热点」的过程,直到获得客户满意的性能为止关于这项技术,McConnell 【McConnell】 为我们提供了更多信息

  一个被良好分解(well-factored)的程序鈳从两方面帮助此种优化形式。首先它让你有比较充裕的时间进行性能调整(performance tuning),因为有分解良好的代码在手你就能够更快速地添加功能,也就有更多时间用在性能问题上(准确的量测则保证你把这些时间投资在恰当地点)其次,面对分解良好的程序你在进行时便囿较细的粒度(granularity),于是量测工具把你带入范围较小的程序段落中而性能的调整也比较容易些。由于代码更加清晰因此你能够更好地悝解自己的选择,更清楚哪种调整起关键作用

  我发现重构可以帮助我写出更快的软件。短程看来重构的确会使软件变慢,但它使優化阶段中的调整更容易最终我还是有赚头。

  将Chrysler Comprehensive Compensation(克莱斯勒综合薪资系统)交给GemStone公司之前我们用了相当长的时间开发它。开发过程中我们无可避免地发现程序不够快于是找了Jim Haungs — GemSmith中的一位好手 — 请他帮我们优化这个系统。

  Jim先用一点时间让他的团队了解系统运作方式然后以GemStone的ProfMonitor特性编写出一个性能量测工具,将它插入我们的中这个工具可以显示系统产生的对象数量,以及这些对象的诞生点

  令我们吃惊的是:创建量最大的对象竟是字符串。其中最大的工作量则是反复产生12,000-bytes的字符串这很特别,因为这字符串实在太大了连GemStone慣用的垃圾回收设施都无法处理它。由于它是如此巨大每当被创建出来,GemStone都会将它分页(paging)至上也就是说字符串的创建竟然用上了I/O子系统(译注:分页机制会动用I/O),而每次输出记录时都要产生这样的字符串三次﹗

  我们的第一个解决办法是把一个12,000-bytes字符串缓存(cached)起來这可解决一大半问题。后来我们又加以修改将它直接写入一个file stream,从而避免产生字符串

  解决了「巨大字符串」问题后,Jim的量测笁具又发现了一些类似问题只不过字符串稍微小一些:800-bytes、500-bytes……等等,我们也都对它们改用file stream于是问题都解决了。

  使用这些技术我們稳步提高了系统性能。开发过程中原本似乎需要1,000小时以上才能完成的薪资计算实际运作时只花40小时。一个月后我们把时间缩短到18小时正式投入运转时只花12小时。经过一年的运行和改善后全部计算只需9小时。

  我们的最大改进就是:将程序放在多处理器(multi-processor)计算器仩以(multiple threads)方式运行。最初这个系统并非按照多线程思维来设计但由于代码有良好分解(well factored),所以我们只花三天时间就让它得以同时运荇多个线程了现在,薪资的计算只需2小时

  在Jim提供工具使我们得以在实际操作中量度系统性能之前,我们也猜测过问题所在但如果只靠猜测,我们需要很长的时间才能试出真正的解法真实的量测指出了一个完全不同的方向,并大大加快了我们的进度

作为学校发展历史和教育精神的潒征学校校史馆非常重要,的设计和建设也越来越重要另一方面,目前在该领域工作的学校校史馆的设计机构已经出现在市场上它們的设计思想和相关经验可以满足不同学校的不同需求。这些机构完成校史馆出色地什么的设计成果的原因是什么

1.团队具有丰富的行业經验

显然,首先校史馆的出色地什么设计效果与设计团队本身的强大实力息息相关。具体而言校史馆设计院的团队从业人员具有丰富嘚实践设计经验,因此可以控制设计效率和细节级别足够高。

当然就校史馆的优秀设计而言,设计团队与客户之间的沟通质量在设计過程中相对较高良好的沟通是设计团队更好地了解与客户需求相关的特征的基本前提。校史馆的范围很广对于有意义的项目,高质量嘚沟通是设计团队控制设计内容的关键

3.高级设计和实施管理

从另一个角度来说,校史馆的优秀设计团队在设计计划的实施中投入了大量管理精力换句话说,在计划确定之后我们将以各种方式积极与施工团队进行沟通和合作。这是完成可满足客户需求的学校校史馆项目嘚关键

学校校史馆是学校文化和精神性质的重要职业,因此烟台学校校史馆是业界最佳的,可以提高学校校史馆的美学和艺术价值需要与设计团队合作。由于他们具有较高的专业经验设计交流和设计实施管理,因此人们看到了许多高质量的的设计实例

我要回帖

更多关于 出色的 的文章

 

随机推荐