随笔

  • 1.简单规则
  • 2.软件的熵
  • 3.石头汤与煮青蛙
    • 3.1 石头汤
    • 3.2 煮青蛙
    • 3.3 小结
  • 4. 知识资产
  • 5. 避免重复
  • 6. 正交性
    • 6.1 非正交系统
    • 6.2 正交的好处
      • 6.2.1 工作中应用正交原则的几种方式。
      • 6.2.2 编码
      • 6.2.3 测试
      • 6.2.3文档
  • 7.可撤消性
    • 7.1 灵活的架构
    • 7.2 挑战
  • 8. [曳光弹](https://blog.csdn.net/ynchyong/article/details/112941234)
    • 8.1 曳光代码方法有许多优点:
    • 8.2 曳光弹并非总能击中目标
    • 8.3 曳光代码 vs. 原型制作
    • 8.4 应制作原型的事物
    • 8.5 怎样使用原型
    • 8.6 制作架构原型
    • 8.7 怎样“不”使用原型

1.简单规则

  1. 注重实效的编程源于注重实效的思考的哲学

  2. 我的源码让猫给吃了
    你没有做备份,那是你的错。告诉你的老板“我的源码让猫给吃了”也无法改变这一点。

  3. 提供各种选择,不要找蹩脚的借口(Provide Options, Don’t Make Lame Excuses)
    在你的头脑里把谈话预演一遍。其他人可能会说什么?他们是否会问:“你试了这个吗……”,或是“你没有考虑那个吗?”你将怎样回答?在你去告诉他们坏消息之前,是否还有其他你可以再试一试的办法?有时,你其实知道他们会说什么,所以还是不要给他们添麻烦吧。

  4. Make Quality a Requirements Issue----使质量成为需求问题

  5. 知道何时止步
    不要因为过度修饰和过于求精而毁损完好的程序。继续前进,让你的代码凭着自己的质量站立一会儿。

  6. 如果你不听他们说话,他们也不会听你说话。

  7. It’s Both What You Say and the Way You Say It—你说什么和你怎么说同样重要

2.软件的熵

熵是一个来自物理学的概念,指的是某个系统中的“无序”的总量。遗憾的是,热力学定律保证了宇宙中的熵倾向于最大化。当软件中的无序增长时,程序员们称之为“软件腐烂”(software rot)。
犯罪和城市衰退领域的研究者发现了一种迷人的触发机制,一种能够很快将整洁、完整和有人居住的建筑变为破败的废弃物的机制--------破窗户
“破窗户理论”启发了纽约和其他大城市的警察部门,他们对一些轻微的案件严加处理,以防止大案的发生。这起了作用:管束破窗户、乱涂乱画和其他轻微违法事件减少了严重罪案的发生。
Don’t Live with Broken Windows(不要容忍破窗户)
不要留着“破窗户”(低劣的设计、错误决策、或是糟糕的代码)不修。发现一个就修一个。如果没有足够的时间进行适当的修理,就用木板把它钉起来。或许你可以把出问题的代码放入注释(comment out),或是显示“未实现”消息,或是用虚设的数据(dummy data)加以替代。采取某种行动防止进一步的损坏,并说明情势处在你的控制之下。
你也许在想,没有人有时间到处清理项目的所有碎玻璃。如果你继续这么想,你就最好计划找一个大型垃圾罐,或是搬到别处去。不要让熵赢得胜利。

在最初得出“破窗户理论”的一项实验中,一辆废弃的轿车放了一个星期,无人理睬。而一旦有一扇窗户被打破,数小时之内车上的设备就被抢夺一空,车也被翻了个底朝天。
按照同样的道理,如果你发现你所在团队和项目的代码十分漂亮——编写整洁、设计良好,并且很优雅——你就很可能会格外注意不去把它弄脏。即使有火在咆哮(最后期限、发布日期、会展演示,等等),你也不会想成为第一个弄脏东西的人。

3.石头汤与煮青蛙

3.1 石头汤

在有些情况下,你也许确切地知道需要做什么,以及怎样去做。整个系统就在你的眼前——你知道它是对的。但请求许可去处理整个事情,你会遇到拖延和漠然。大家要设立委员会,预算需要批准,事情会变得复杂化。每个人都会护卫他们自己的资源。有时候,这叫做“启动杂役”(start-up fatigue)。
  这正是拿出石头的时候(石头汤故事)。设计出你可以合理要求的东西,好好开发它。一旦完成,就拿给大家看,让他们大吃一惊。然后说:“要是我们增加……可能就会更好。”假装那并不重要。坐回椅子上,等着他们开始要你增加你本来就想要的功能。人们发现,参与正在发生的成功要更容易。让他们瞥见未来,你就能让他们聚集在你周围

Be a Catalyst for Change-----做变化的催化剂

村民的角度
  另一方面,石头汤的故事也是关于温和而渐进的欺骗的故事。它讲述的是过于集中的注意力。村民想着石头,忘了世界的其余部分。我们都是这样,每一天。事情会悄悄爬到我们身上。
  我们都看见过这样的症状。项目慢慢地、不可改变地完全失去控制。大多数软件灾难都是从微不足道的小事情开始的,大多数项目的拖延都是一天一天发生的。系统一个特性一个特性地偏离其规范,一个又一个的补丁被打到某段代码上,直到最初的代码一点没有留下。常常是小事情的累积破坏了士气和团队。

3.2 煮青蛙

Remember the Big Picture — 记住大图景

青蛙的问题与破窗户问题不同。在破窗户理论中,人们失去与熵战斗的意愿,是因为他们觉察到没有人会在意。而青蛙只是没有注意到变化

不要像青蛙一样。留心大图景。要持续不断地观察周围发生的事情,而不只是你自己在做的事情。

3.3 小结

士兵渐进地欺骗村民,但他们所催生的变化对村民完全有利。但是,渐进地欺骗青蛙,你是在加害于它。当你设法催生变化时,你能否确定你是在做石头汤还是青蛙汤?

4. 知识资产

经营你的资产, 定期投资,多元化
Critically Analyze What You Read and Hear ---- 批判地分析你读到的和听到的
批判地思考你读到的和听到的。你需要确保你的资产中的知识是准确的,并且没有受到供应商或媒体炒作的影响。警惕声称他们的信条提供了惟一答案的狂热者——那或许适用、或许不适用于你和你的项目。 
不要低估商业主义的力量。Web搜索引擎把某个页面列在最前面,并不意味着那就是最佳选择;内容供应商可以付钱让自己排在前面。书店在显著位置展示某一本书,也并不意味着那就是一本好书,甚至也不说明那是一本受欢迎的书;它们可能是付了钱才放在那里的。

5. 避免重复

重复都可归入下列范畴:

  • 加的重复(imposed duplication)。开发者觉得他们无可选择——环境似乎要求重复。
     有时,重复似乎是强加给我们的。项目标准可能要求建立含有重复信息的文档,或是重复代码中的信息的文档。多个目标平台各自需要自己的编程语言、库以及开发环境,这会使我们重复共有的定义和过程。编程语言自身要求某些重复信息的结构。我们都在我们觉得无力避免重复的情形下工作过。然而也有一些方法,可用于把一项知识存放在一处,以遵守DRY原则,同时也让我们的生活更容易一点。DRY法则告诉我们,要把低级的知识放在代码中,它属于那里;把注释保留给其他的高级说明。否则,我们就是在重复知识,而每一次改变都意味着既要改变代码,也要改变注释。注释将不可避免地变得过时,而不可信任的注释比完全没有注释更糟。
  • 无意的重复(inadvertent duplication)。开发者没有意识到他们在重复信息。
    有时,重复来自设计中的错误
    让我们看一个来自配送行业的例子。假定我们的分析揭示,一辆卡车有车型、牌照号、司机及其他一些属性。与此类似,发运路线的属性包括路线、卡车和司机。基于这一理解,我们编写了一些类。
      但如果Sally打电话请病假、我们必须改换司机,事情又会怎样呢?Truck和DeliverRoute都包含有司机。我们改变哪一个?显然这样的重复很糟糕。根据底层的商业模型对其进行规范化(normalize)——卡车的底层属性集真的应包含司机?路线呢?又或许我们需要第三种对象,把司机、卡车及路线结合在一起。不管最终的解决方案是什么,我们都应避免这种不规范的数据。
   class Line {public:Point  start;Point  end;double length;};

第一眼看上去,这个类似乎是合理的。线段显然有起点和终点,并总是有长度(即使长度为零)。但这里有重复。长度是由起点和终点决定的:改变其中一个,长度就会变化。最好是让长度成为计算字段
在以后的开发过程中,你可以因为性能原因而选择违反DRY原则。这经常会发生在你需要缓存数据,以避免重复昂贵的操作时。其诀窍是使影响局部化对DRY原则的违反没有暴露给外界:只有类中的方法需要注意“保持行为良好”

  • 无耐性的重复(impatient duplication)。开发者偷懒,他们重复,因为那样似乎更容易。
    每个项目都有时间压力——这是能够驱使我们中间最优秀的人走捷径的力量。
    无耐性的重复是一种容易检测和处理的重复形式,但那需要你接受训练,并愿意为避免以后的痛苦而预先花一些时间。
    需要与你写过的一个例程相似的例程?你会受到诱惑,去拷贝原来的代码,并做出一些改动。需要一个表示最大点数的值?如果我改动头文件,整个项目就得重新构建。也许我应该在这里使用直接的数字(literal number),这里,还有这里,需要一个与Java runtime中的某个类相似的类?源码在那里(你有使用许可),那么为什么不拷贝它、并做出你所需的改动呢?
      如果你觉得受到诱惑,想一想古老的格言:“欲速则不达”。你现在也许可以节省几秒钟,但以后却可能损失几小时。想一想围绕着Y2K惨败的种种问题。其中许多问题是由开发者的懒惰造成的:他们没有参数化日期字段的尺寸,或是实现集中的日期服务库。
  • 开发者之间的重复(interdeveloper duplication)。同一团队(或不同团队)的几个人重复了同样的信息。
    或许是最难检测和处理的重复发生在项目的不同开发者之间。整个功能集都可能在无意中被重复,而这些重复可能几年里都不会被发现,从而导致各种维护问题。我们亲耳听说过,美国某个州在对政府的计算机系统进行Y2K问题检查时,审计者发现有超出10,000个程序,每一个都有自己的社会保障号验证代码。
    处理这个问题的最佳方式是鼓励开发者相互进行主动的交流。设置论坛,用以讨论常见问题.让某个团队成员担任项目资料管理员,其工作是促进知识的交流。在源码树中指定一个中央区域,用于存放实用例程和脚本。一定要阅读他人的源码与文档,不管是非正式的,还是进行代码复查。你不是在窥探——你是在向他们学习。而且要记住,访问是互惠的——不要因为别人钻研(乱钻?)你的代码而苦恼。

Make It Easy to Reuse ---- 让复用变得容易
你所要做的是营造一种环境,在其中要找到并复用已有的东西,比自己编写更容易。如果不容易,大家就不会去复用。而如果不进行复用,你们就会有重复知识的风险。

6. 正交性

如果你想要制作易于设计、构建、测试及扩展的系统,正交性是一个十分关键的概念
  “正交性”是从几何学中借来的术语。如果两条直线相交成直角,它们就是正交的,比如图中的坐标轴。用向量术语说,这两条直线互不依赖。沿着某一条直线移动,你投影到另一条直线上的位置不变。
  在计算技术中,该术语用于表示某种不相依赖性或是解耦性。如果两个或更多事物中的一个发生变化,不会影响其他事物,这些事物就是正交的。在设计良好的系统中,数据库代码与用户界面是正交的:你可以改动界面,而不影响数据库;更换数据库,而不用改动界面。

6.1 非正交系统

你正乘坐直升机游览科罗拉多大峡谷,驾驶员——他显然犯了一个错误,在吃鱼,他的午餐——突然呻吟起来,晕了过去。幸运的是,他把你留在了离地面100英尺的地方。你推断,升降杆控制总升力,所以轻轻将其压低可以让直升机平缓降向地面。然而,当你这样做时,却发现生活并非那么简单。直升机的鼻子向下,开始向左盘旋下降。突然间你发现,你驾驶的这个系统,所有的控制输入都有次级效应。压低左手的操作杆,你需要补偿性地向后移动右手柄,并踩右踏板。但这些改变中的每一项都会再次影响所有其他的控制。突然间,你在用一个让人难以置信的复杂系统玩杂耍,其中每一项改变都会影响所有其他的输入。你的工作负担异常巨大:你的手脚在不停地移动,试图平衡所有交互影响的力量。

6.2 正交的好处

如直升机的例子所阐明的,非正交系统的改变与控制更复杂是其固有的性质。当任何系统的各组件互相高度依赖时,就不再有局部修正(local fix)这样的事情。

Eliminate Effects Between Unrelated Things ---- 消除无关事物之间的影响

我们想要设计自足(self-contained)的组件:独立,具有单一、良好定义的目的(Yourdon和Constantine称之为内聚(cohesion)[YC86])。如果组件是相互隔离的,你就知道你能够改变其中之一,而不用担心其余组件。只要你不改变组件的外部接口,你就可以放心:你不会造成波及整个系统的问题。

如果你编写正交的系统,你得到两个主要好处:提高生产率与降低风险。

  • 提高生产率
    改动得以局部化,所以开发时间和测试时间得以降低。与编写单个的大块代码相比,编写多个相对较小的、自足的组件更为容易。你可以设计、编写简单的组件,对其进行单元测试,然后把它们忘掉——当你增加新代码时,无须不断改动已有的代码。
    正交的途径还能够促进复用。如果组件具有明确而具体的、良好定义的责任,就可以用其最初的实现者未曾想象过的方式,把它们与新组件组合在一起。
    如果你对正交的组件进行组合,生产率会有相当微妙的提高。假定某个组件做M件事情,而另一个组件做N件事情。如果它们是正交的,而你把它们组合在一起,结果就能做M x N件事情。但是,如果这两个组件是非正交的,它们就会重叠,结果能做的事情就更少。通过组合正交的组件,你的每一份努力都能得到更多的功能。

  • 降低风险
     正交的途径能降低任何开发中固有的风险。
    有问题的代码区域被隔离开来。如果某个模块有毛病,它不大可能把病症扩散到系统的其余部分。要把它切掉,换成健康的新模块也更容易。
    所得系统更健壮。对特定区域做出小的改动与修正,你所导致的任何问题都将局限在该区域中。
    正交系统很可能能得到更好的测试,因为设计测试、并针对其组件运行测试更容易。
    你不会与特定的供应商、产品、或是平台紧绑在一起,因为与这些第三方组件的接口将被隔离在全部开发的较小部分中。

6.2.1 工作中应用正交原则的几种方式。

  1. 项目团队

你是否注意到,有些项目团队很有效率,每个人都知道要做什么,并全力做出贡献,而另一些团队的成员却老是在争吵,而且好像无法避免互相妨碍?
  这常常是一个正交性问题。如果团队的组织有许多重叠,各个成员就会对责任感到困惑。每一次改动都需要整个团队开一次会,因为他们中的任何一个人都可能受到影响。

怎样把团队划分为责任得到了良好定义的小组,并使重叠降至最低呢?没有简单的答案。这部分地取决于项目本身,以及你对可能变动的区域的分析。这还取决于你可以得到的人员。我们的偏好是从使基础设施与应用分离开始。每个主要的基础设施组件(数据库、通信接口、中间件层,等等)有自己的子团队。如果应用功能的划分显而易见,那就照此划分。然后我们考察我们现有的(或计划有的)人员,并对分组进行相应的调整。
  你可以对项目团队的正交性进行非正式的衡量。只要看一看,在讨论每个所需改动时需要涉及多少人。人数越多,团队的正交性就越差。显然,正交的团队效率也更高(尽管如此,我们也鼓励子团队不断地相互交流)。

大多数开发者都熟知需要设计正交的系统,尽管他们可能会使用像模块化、基于组件、或是分层这样的术语描述该过程。系统应该由一组相互协作的模块组成,每个模块都实现不依赖于其他模块的功能。有时,这些组件被组织为多个层次,每层提供一级抽象。这种分层的途径是设计正交系统的强大方式。因为每层都只使用在其下面的层次提供的抽象,在改动底层实现、而又不影响其他代码方面,你拥有极大的灵活性。分层也降低了模块间依赖关系失控的风险。

对于正交设计,有一种简单的测试方法。一旦设计好组件,问问你自己:如果我显著地改变某个特定功能背后的需求,有多少模块会受影响?在正交系统中,答案应该是“一个”。移动GUI面板上的按钮,不应该要求改动数据库schema。增加语境敏感的帮助,也不应该改动记账子系统。

  1. 工具箱与库
    需要一段Java代码,既运行在本地的服务器机器上,又运行在远程的客户机器上。要把类按这样的方式分布,可以选用RMI或CORBA。如果用RMI实现类的远程访问,对类中的远程方法的每一次调用都可能会抛出异常;这意味着,一个幼稚的实现可能会要求我们,无论何时使用远程类,都要对异常进行处理。在这里,使用RMI显然不是正交的:调用远程类的代码应该不用知道这些类的位置。另一种方法——使用CORBA——就没有施加这样的限制:我们可以编写不知道我们类的位置的代码。
    在引入某个工具箱时(甚或是来自你们团队其他成员的库),问问你自己,它是否会迫使你对代码进行不必要的改动。如果对象持久模型(object persistence scheme)是透明的,那么它就是正交的。如果它要求你以一种特殊的方式创建或访问对象,那么它就不是正交的。让这样的细节与代码隔离具有额外的好处:它使得你在以后更容易更换供应商。
      Enterprise Java Beans(EJB)系统是正交性的一个有趣例子。在大多数面向事务的系统中,应用代码必须描述每个事务的开始与结束。在EJB中,该信息是作为元数据,在任何代码之外,以声明的方式表示的。同一应用代码不用修改,就可以运行在不同的EJB事务环境中。这很可能是将来许多环境的模型。
      
      正交性的另一个有趣的变体是面向方面编程(Aspect-Oriented Programming,AOP),这是Xerox Parc的一个研究项目([KLM+97]与[URL 49])。AOP让你在一个地方表达本来会分散在源码各处的某种行为。例如,日志消息通常是在源码各处、通过显式地调用某个日志函数生成的。通过AOP,你把日志功能正交地实现到要进行日志记录的代码中。使用AOP的Java版本,你可以通过编写aspect、在进入类Fred的任何方法时写日志消息
       如果你把这个方面编织(weave)进你的代码,就会生成追踪消息。否则,你就不会看到任何消息。不管怎样,你原来的源码都没有变化。

6.2.2 编码

每次你编写代码,都有降低应用正交性的风险。除非你不仅时刻监视你正在做的事情,也时刻监视应用的更大语境,否则,你就有可能无意中重复其他模块的功能,或是两次表示已有的知识

你可以将若干技术用于维持正交性:

  1. 让你的代码保持解耦。编写“羞怯”的代码——也就是不会没有必要地向其他模块暴露任何事情、也不依赖其他模块的实现的模块。“解耦与迪米特法则(Law of Demeter)”,如果你需要改变对象的状态,让这个对象替你去做。这样,你的代码就会保持与其他代码的实现的隔离,并增加你保持正交的机会。
  2. 避免使用全局数据。每当你的代码引用全局数据时,它都把自己与共享该数据的其他组件绑在了一起。即使你只想对全局数据进行读取,也可能会带来麻烦(例如,如果你突然需要把代码改为多线程的)。一般而言,如果你把所需的任何语境(context)显式地传入模块,你的代码就会更易于理解和维护。在面向对象应用中,语境常常作为参数传给对象的构造器。换句话说,你可以创建含有语境的结构,并传递指向这些结构的引用。
  3. 《设计模式》[GHJV95]一书中的Singleton(单体)模式是确保特定类的对象只有一个实例的一种途径。许多人把这些singleton对象用作某种全局变量(特别是在除此而外不支持全局概念的语言中,比如Java)。使用singleton要小心——它们可能造成不必要的关联。
  4. 避免编写相似的函数。你常常会遇到看起来全都很像的一组函数——它们也许在开始和结束处共享公共的代码,中间的算法却各有不同。重复的代码是结构问题的一种症状。要了解更好的实现,参见《设计模式》一书中的Strategy(策略)模式。
  5. 养成不断地批判对待自己的代码的习惯。寻找任何重新进行组织、以改善其结构和正交性的机会。这个过程叫做重构(refactoring),它非常重要.

6.2.3 测试

正交地设计和实现的系统也更易于测试,因为系统的各组件间的交互是形式化的和有限的,更多的系统测试可以在单个的模块级进行。这是好消息,因为与集成测试(integration testing)相比,模块级(或单元)测试要更容易规定和进行得多。事实上,我们建议让每个模块都拥有自己的、内建在代码中的单元测试,并让这些测试作为常规构建过程的一部分自动运行.
构建单元测试本身是对正交性的一项有趣测试。要构建和链接某个单元测试,都需要什么?只是为了编译或链接某个测试,你是否就必须把系统其余的很大一部分拽进来?如果是这样,你已经发现了一个没有很好地解除与系统其余部分耦合的模块。
修正bug也是评估整个系统的正交性的好时候。当你遇到问题时,评估修正的局部化程度。

你是否只改动了一个模块,或者改动分散在整个系统的各个地方?当你做出改动时,它修正了所有问题,还是又神秘地出现了其他问题?这是开始运用自动化的好机会。如果你使用了源码控制系统,当你在测试之后、把代码签回(check the code back)时,标记所做的bug修正。随后你可以运行月报,分析每个bug修正所影响的源文件数目的变化趋势。

6.2.3文档

也许会让人惊讶,正交性也适用于文档。其坐标轴是内容和表现形式。对于真正正交的文档,你应该能显著地改变外观,而不用改变内容。现代的字处理器提供了样式表和宏,能够对你有帮助
认同正交性
  正交性DRY原则紧密相关。运用DRY原则,你是在寻求使系统中的重复降至最小;运用正交性原则,你可降低系统的各组件间的相互依赖。这样说也许有点笨拙,但如果你紧密结合DRY原则、运用正交性原则,你将会发现你开发的系统会变得更为灵活、更易于理解、并且更易于调试、测试和维护。
  如果你参加了一个项目,大家都在不顾一切地做出改动,而每一处改动似乎都会造成别的东西出错,回想一下直升机的噩梦。项目很可能没有进行正交的设计和编码。是重构的时候了。

7.可撤消性

如果某个想法是你惟一的想法,再没有什么比这更危险的事情了。
——Emil-Auguste Chartier, Propos sur la religion, 1938

工程师们喜欢问题有简单、单一的解决方案。与论述法国大革命的无数起因的一篇模糊、热烈的文章相比,允许你怀着极大的自信宣称x = 2的数学测验要让人觉得舒服得多。管理人员往往与工程师趣味相投:单一、容易的答案正好可以放在电子表格和项目计划中。
  现实世界能够合作就好了!遗憾的是,今天x是2,明天也许就需要是5,下周则是3。没有什么永远不变——而如果你严重依赖某一事实,你几乎可以确定它将会变化。
  要实现某种东西,总有不止一种方式,而且通常有不止一家供应商可以提供第三方产品。如果你参与的项目被短视 、认为只有一种实现方式的观念所牵绊,你也许就会遇到让人不悦的意外之事。许多项目团队会被迫在未来展现之时睁开眼睛:
“但你说过我们要使用XYZ数据库!我们的项目已经完成了85%的编码工作。我们现在不能改变了!”程序员抗议道。“对不起,但我们公司决定进行标准化,改用PDQ数据库——所有项目。这超出了我的职权范围。我们必须重新编码。周末所有人都要加班,直到另行通知为止。”
  变动不一定会这么严苛,甚至也不会这么迫在眉睫。但随着时间的流逝,随着你的项目取得进展,你也许会发现自己陷在无法立足的处境里。随着每一项关键决策的做出,项目团队受到越来越小的目标的约束——现实的更窄小的版本,选择的余地越来越小。
  在许多关键决策做出之后,目标会变得如此之小,以至于如果它动一下,或是风改变方向,或是东京的蝴蝶扇动翅膀,你都会错过目标。而且你可能会偏出很远。
 问题在于,关键决策不容易撤消
  一旦你决定使用这家供应商的数据库、那种架构模式、或是特定的部署模型(例如,客户-服务器 vs. 单机),除非付出极大的代价,否则你就将受制于一个无法撤消的动作进程(course of action)。

我们让本书的许多话题相互配合,以制作灵活、有适应能力的软件。通过遵循它们的建议——特别是DRY原则、解耦以及元数据的使用——我们不必做出许多关键的、不可逆转的决策。这是一件好事情,因为我们并非总能在一开始就做出最好的决策。我们采用了某种技术,却发现我们雇不到足够的具有必需技能的人。我们刚刚选定某个第三方供应商,他们就被竞争者收购了。与我们开发软件的速度相比,需求、用户以及硬件变得更快。

假定在项目初期,你决定使用供应商A提供的关系数据库。过了很久,在性能测试过程中,你发现数据库简直太慢了,而供应商B提供的对象数据库更快。对于大多数传统项目,你不会有什么运气。大多数时候,对第三方产品的调用都缠绕在代码各处。但如果你真的已经把数据库的概念抽象出来——抽象到数据库只是把持久(persistence)作为服务提供出来的程度——你就会拥有“中流换马(change horses in midstream)”的灵活性。
  与此类似,假定项目最初采用的是客户-服务器模型,但随即,在开发的后期,市场部门认为服务器对于某些客户过于昂贵,他们想要单机版。对你来说,那会有多困难?因为这只是一个部署问题,所以不应该要很多天。如果所需时间更长,那么你就没有考虑过可撤消性。另外一个方向甚至更有趣。如果需要以客户-服务器或n层方式部署你正在开发的单机产品,事情又会怎样?那也不应该很困难。

错误在于假定决策是浇铸在石头上的——同时还在于没有为可能出现的意外事件做准备。

要把决策视为是写在沙滩上的,而不要把它们刻在石头上。大浪随时可能到来,把它们抹去。

There Are No Final Decisions 不存在最终决策

7.1 灵活的架构

有许多人会设法保持代码的灵活性,而你还需要考虑维持架构、部署及供应商集成等领域的灵活性。
  像CORBA这样的技术可以帮助把项目的某些部分与开发语言或平台的变化隔离开来。Java在该平台上的性能不能满足要求?重新用C++编写客户代码,其他没有什么需要改变。用C++编写的规则引擎不够灵活?换到Smalltalk版本。采用CORBA架构,你只须改动替换的组件:其他组件应该不会受影响。
  你正在开发UNIX软件?哪一种?你是否处理了所有可移植性问题?你正在为某个特定版本的Windows做开发?哪一种——3.1、95、98、NT、CE、或是2000?支持其他版本有多难?如果你让决策保持软和与柔韧,事情就完全不困难。如果在代码中有着糟糕的封装、高度耦合以及硬编码的逻辑或参数,事情也许就是不可能的。
  不确定市场部门想怎样部署系统?预先考虑这个问题,你可以支持单机、客户-服务器、或n层模型——只需要改变配置文件。我们就写过一些这么做的程序。
  通常,你可以把第三方产品隐藏在定义良好的抽象接口后面。事实上,在我们做过的任何项目中,我们都总能够这么做。但假定你无法那么彻底地隔离它,如果你必须大量地把某些语句分散在整个代码中,该怎么办?把该需求放入元数据,并且使用某种自动机制——比如Aspect或Perl——把必需的语句插入代码自身中。无论你使用的是何种机制,让它可撤消。如果某样东西是自动添加的,它也可以被自动去掉。
  没有人知道未来会怎样,尤其是我们!所以要让你的代码学会“摇滚”:可以“摇”就“摇”,必须“滚”就“滚”。

7.2 挑战

让我们通过“薛定谔的猫”学一点量子力学。假定在一个封闭的盒子里有一只猫,还有一个放射性粒子。这个粒子正好有50%的机会裂变成两个粒子。如果发生了裂变,猫就会被杀死;如果没有,猫就不会有事。那么,猫是死是活?根据薛定谔的理论,正确的答案是“都是”。每当有两种可能结果的亚核反应发生时,宇宙就会被克隆。在其中一个宇宙中,事件发生;在另一个宇宙中,事件不发生。猫在一个宇宙中是活的,在另一个宇宙中是死的。只有当你打开盒子,你才知道你在哪一个宇宙里。

怪不得为未来编码很困难。

但想一想,代码沿着与装满薛定谔的猫的盒子一样的路线演化:每一项决策都会导致不同版本的未来。你的代码能支持多少种可能的未来?哪一种未来更有可能发生?到时支持它们有多困难?

你敢打开盒子吗?

8. 曳光弹

Use Tracer Bullets to Find the Target 用曳光弹找到目标

一个复杂的客户-服务器数据库营销项目。其部分需求是要能够指定并执行临时查询。服务器是一系列专用的关系数据库。用Object Pascal编写的客户GUI使用一组C库提供给服务器的接口。在转换为优化的SQL之前,用户的查询以类似Lisp的表示方式存储在服务器上;转换直到执行前才进行。有许多未知因素和许多不同的环境,没有人清楚地知道GUI应该怎样工作。

这是使用曳光代码的好机会。我们开发了前端框架、用于表示查询的库以及用于把所存储的查询转换为具体数据库的查询的结构。随后我们把它们集中在一起,并检查它们是否能工作。使用最初构建的系统,我们所能做的只是提交一个查询,列出某个表中的所有行,但它证明了UI能够与库交谈,库能够对查询进行序列化和解序列化,而服务器能够根据结果生成SQL。在接下来的几个月里,我们逐渐充实这个基本结构,通过并行地扩大曳光代码的各个组件增加新的功能。当UI增加了新的查询类型时,库随之成长,而我们也使SQL生成变得更为成熟。

曳光代码并非用过就扔的代码:你编写它,是为了保留它。它含有任何一段产品代码都拥有的完整的错误检查、结构、文档、以及自查。它只不过功能不全而已。但是,一旦你在系统的各组件间实现了端到端(end-to-end)的连接,你就可以检查你离目标还有多远,并在必要的情况下进行调整。一旦你完全瞄准,增加功能将是一件容易的事情。

曳光开发与项目永不会结束的理念是一致的:总有改动需要完成,总有功能需要增加。这是一个渐进的过程。
  
  另一种传统做法是一种繁重的工程方法:把代码划分为模块,在真空中对模块进行编码。把模块组合成子配件(subassembly),再对子配件进行组合,直到有一天你拥有完整的应用为止。直到那时,才能把应用作为一个整体呈现给用户,并进行测试。

8.1 曳光代码方法有许多优点:

  1. 用户能够及早看到能工作的东西。如果你成功地就你在做的事情与用户进行了交流 ,用户就会知道他们看到的是还未完成的东西。他们不会因为缺少功能而失望;他们将因为看到了系统的某种可见的进展而欣喜陶醉。他们还会随着项目的进展做出贡献,增加他们的“买入”。同样是这些用户,他们很可能也会告诉你,每一轮“射击”距离目标有多接近。

  2. 开发者构建了一个他们能在其中工作的结构。最令人畏缩的纸是什么也没有写的白纸。如果你已经找出应用的所有端到端的交互,并把它们体现在代码里,你的团队就无须再无中生有。这让每个人都变得更有生产力,同时又促进了一致性。

  3. 你有了一个集成平台。随着系统端到端地连接起来,你拥有了一个环境,一旦新的代码段通过了单元测试,你就可以将其加入该环境中。你将每天进行集成(常常是一天进行多次),而不是尝试进行大爆炸式的集成。每一个新改动的影响都更为显而易见,而交互也更为有限,于是调试和测试将变得更快、更准确。

  4. 你有了可用于演示的东西。项目出资人与高级官员往往会在最不方便的时候来看演示。有了曳光代码,你总有东西可以拿给他们看。

  5. 你将更能够感觉到工作进展。在曳光代码开发中,开发者一个一个地处理用例(use case)。做完一个,再做下一个。评测性能、并向用户演示你的进展,变得容易了许多。因为每一项个别的开发都更小,你也避免了创建这样的整体式代码块:一周又一周,其完成度一直是95%。

8.2 曳光弹并非总能击中目标

曳光弹告诉你击中的是什么。那不一定总是目标。于是你调整准星,直到完全击中目标为止。这正是要点所在。
  曳光代码也是如此。你在不能100%确定该去往何处的情形下使用这项技术。如果最初的几次尝试错过了目标——用户说:“那不是我的意思”,你需要的数据在你需要它时不可用,或是性能好像有问题——你不应感到惊奇。找出怎样改变已有的东西、让其更接近目标的办法,并且为你使用了一种简约的开发方法而感到高兴。小段代码的惯性也小——要改变它更容易、更迅速。你能够搜集关于你的应用的反馈,而且与其他任何方法相比,你能够花费较少代价、更为迅速地生成新的、更为准确的版本。同时,因为每个主要的应用组件都已表现在你的曳光代码中,用户可以确信,他们所看到的东西具有现实基础,不仅仅是纸上的规范。

8.3 曳光代码 vs. 原型制作

你也许会想,这种曳光代码的概念就是原型制作,只不过有一个更富“进攻性”的名字。它们有区别。使用原型,你是要探究最终系统的某些具体的方面。使用真正的原型,在对概念进行了试验之后,你会把你捆扎在一起的无论什么东西扔掉,并根据你学到的经验教训重新适当地进行编码。
  例如,假定你在制作一个应用,其用途是帮助运货人确定怎样把不规则的箱子装入集装箱。
除了考虑其他一些问题,你还需要设计直观的用户界面,而你用于确定最优装箱方式的算法非常复杂。
  你可以在GUI工具中为最终用户制作一个用户界面原型。你的代码只能让界面响应用户操作。一旦用户对界面布局表示同意,你可以把它扔掉,用目标语言重新对其进行编码,并在其后加上商业逻辑。与此类似,你可以为实际进行装箱的算法制作原型。你可以用像Perl这样的宽松的高级语言编写功能测试,并用更接近机器的某种语言编写低级的性能测试。无论如何,一旦你做出决策,你都会重新开始在其最终环境中为算法编写代码,与现实世界接合。这就是原型制作,它非常有用。
  曳光代码方法处理的是不同的问题。你需要知道应用怎样结合成一个整体。你想要向用户演示,实际的交互是怎样工作的,同时你还想要给出一个架构骨架,开发者可以在其上增加代码。在这样的情况下,你可以构造一段曳光代码,其中含有一个极其简单的集装箱装箱算法实现(也许是像“先来先服务”这样的算法)和一个简单、但却能工作的用户界面。一旦你把应用中的所有组件都组合在一起,你就拥有了一个可以向你的用户和开发者演示的框架。接下来的时间里,你给这个框架增加新功能,完成预留了接口的例程。但框架仍保持完整,而你也知道,系统将会继续按照你第一次的曳光代码完成时的方式工作。
   其间的区别很重要,足以让我们再重复一次。原型制作生成用过就扔的代码。曳光代码虽然简约,但却是完整的,并且构成了最终系统的骨架的一部分。你可以把原型制作视为在第一发曳光弹发射之前进行的侦察和情报搜集工作。

原型的设计目的就是回答一些问题,所以与投入使用的产品应用相比,它们的开发要便宜得多、快捷得多。其代码可以忽略不重要的细节——在此刻对你不重要,但对后来的用户可能非常重要。例如,如果你在制作GUI原型,你不会因不正确的结果或数据而遭到指责。而另一方面,如果你只是在研究计算或性能方面的问题,你也不会因为相当糟糕的GUI而遭到指责;甚至也可以完全不要GUI。

8.4 应制作原型的事物

你可以选择通过原型来研究什么样的事物呢?任何带有风险的事物。以前没有试过的事物,或是对于最终系统极端关键的事物。任何未被证明的、实验性的、或有疑问的事物。任何让你觉得不舒服的事物。你可以为下列事物制作原型:

  • 架构
  • 已有系统中的新功能
  • 外部数据的结构或内容
  • 第三方工具或组件
  • 性能问题
  • 用户界面设计

Prototype to Learn — 为了学习而制作原型

8.5 怎样使用原型

在构建原型时,你可以忽略哪些细节?

  • 正确性。你也许可以在适当的地方使用虚设的数据。
  • 完整性。原型也许只能在非常有限的意义上工作,也许只有一项预先选择的输入数据和一个菜单项。
  • 健壮性。错误检查很可能不完整,或是完全没有。如果你偏离预定路径,原型就可能崩溃,并在“烟火般的灿烂显示中焚毁”。这没有关系。
  • 风格。在纸上承认这一点让人痛苦,但原型代码可能没有多少注释或文档。根据使用原型的经验,你也许会撰写出大量文档,但关于原型系统自身的内容相对而言却非常少。

因为原型应该遮盖细节,并聚焦于所考虑系统的某些具体方面,你可以用非常高级的语言实现原型——比项目的其余部分更高级(也许是像Perl、Python或Tcl这样的语言)。高级的脚本语言能让你推迟考虑许多细节(包括指定数据类型),并且仍然能制作出能工作的(即使不完整或速度慢)代码。如果你需要制作用户界面的原型,可研究像Tcl/Tk、Visual Basic、Powerbuilder或Delphi这样的工具。
  作为能把低级的部分组合在一起的“胶合剂”,脚本语言工作良好。在Windows下,Visual Basic可以把COM控件胶合在一起。更一般地说,你可以使用像Perl和Python这样的语言,把低级的C库绑在一起——无论是手工进行,还是通过工具自动进行,比如可以自由获取的SWIG[URL 28]。采用这种方法,你可以快速地把现有组件装配进新的配置,从而了解它们的工作情况。

8.6 制作架构原型

许多原型被构造出来,是要为在考虑之下的整个系统建模。与曳光弹不同,在原型系统中,单个模块不需要能行使特定的功能。事实上,要制作架构原型,你甚至不一定需要进行编码——你可以用便笺或索引卡片、在白板上制作原型。你寻求的是了解系统怎样结合成为一个整体,并推迟考虑细节。下面是一些你可以在架构原型中寻求解答的具体问题:

  • 主要组件的责任是否得到了良好定义?是否适当?
  • 主要组件间的协作是否得到了良好定义?
  • 耦合是否得以最小化?
  • 你能否确定重复的潜在来源?
  • 接口定义和各项约束是否可接受?
  • 每个模块在执行过程中是否能访问到其所需的数据?是否能在需要时进行访问?

根据我们制作原型的经验,最后一项往往会产生最让人惊讶和最有价值的结果。

8.7 怎样“不”使用原型

在你着手制作任何基于代码的原型之前,先确定每个人都理解你正在编写用过就扔的代码。对于不知道那只是原型的人,原型可能会具有欺骗性的吸引力。你必须非常清楚地说明,这些代码是用过就扔的,它们不完整,也不可能完整。

别人很容易被演示原型外表的完整性误导,而如果你没有设定正确的期望值,项目出资人或管理部门可能会坚持要部署原型(或其后裔)。提醒他们,你可以用轻木和胶带制造一辆了不起的新车原型,但你却不会在高峰时间的车流中驾驶它。

如果你觉得在你所在的环境或文化中,原型代码的目的很有可能被误解,你也许最好还是采用曳光弹方法。你最后将得到一个坚实的框架,为将来的开发奠定基础。

适当地使用原型,可以帮助你在开发周期的早期确定和改正潜在的问题点——在此时改正错误既便宜、又容易——从而为你节省大量时间、金钱,并大大减轻你遭受的痛苦和折磨。

读书之一--《程序员修炼之道》相关推荐

  1. 读书笔记-程序员修炼之道-序

    前言 我们应该成为什么样的程序员 注重实效的程序员具备的特征 注重实效的个体大型的团队 它是一个持续的过程 前言 程序员修炼之道这本书已经通读了一遍,获益良多,但还是不甚理解,所以在重读一遍,顺便做一 ...

  2. 读书笔记程序员修炼之道(英文版2版)一习惯

    1.就像金融投资一样.每年都要向自己的知识储备库中投资一些知识,下面是一些投资的方向目标: 每年至少学习一门计算机语言:因为每种语言是为了解决不同问题才研究出来的.这样会拓展你的思路,不至于墨守成规: ...

  3. 读书笔记-程序员修炼之道-注重实效的哲学(二)

    石头汤与煮青蛙 村民的角度 足够好的软件 知道何时止步 石头汤与煮青蛙 三个士兵从战场返回家乡,在路上饿了.他们看见前面有村庄,就来了精神-他们相信村民会给他们一顿饭吃.但是当他们到达那里,却发现门锁 ...

  4. 曳光弹 曳光代码 原型制作 读《读程序员修炼之道》

    最近读书<程序员修炼之道:从小工到专家>,看书到第2章 注重实效的途径中,从第10小节曳光弹,到11小节 原型与便笺这两节,相对于整本书中一直都比较顺畅的理解,相对而言这一节可以说是翻译的 ...

  5. 《程序员修炼之道》读书笔记(二)--第三周

    <!doctype html> The pragmatic programmer(2).md 程序员修炼之道(二) 读书笔记--第三周 第二章 注重实效的途径 DRY原则:系统中的每一项只 ...

  6. 《程序员修炼之道–从小工到专家》读书笔记

    <程序员修炼之道–从小工到专家>的读书笔记 <程序员修炼之道–从小工到专家>[美]Andrew Hunt / David Thomas 著 马维达 译 看这本书主要使用了检视阅 ...

  7. 读书之《程序员修炼之道》

    读书分享<程序员修炼之道> 0.0几篇序 读书之前首先看到的内容,让大家知道这不是一本烂书,本书从项目管理,软件架构和设计.代码编写和测试,各方面都有精彩的阐述.序中的重点内容: < ...

  8. 做靠谱的程序员--《程序员修炼之道》读书报告

    这两天花了点时间把<程序员修炼之道>这本书读了,本来估计要一周时间才能读完,读了才发现作者绝对是人才啊,书写的生动有趣,一口气就读完了.随便摘录一下. 1.做一个靠谱的程序员,纯粹的程序员 ...

  9. 程序员修炼之道---读书随笔1

    终于开始读<程序员修炼之道>这本书了,初看这本书的名字,有点以前的道士修炼法术的意思,觉得很是好奇,作为一名程序员,该如何修炼我们自己呢? 这本书涵盖的主题从个人责任.职业发展,直到用于使 ...

  10. 第三周读书笔记《程序员修炼之道》

    这周我阅读的书目是<程序员修炼之道>,重点看了几个章节,但是可能是因为我还没有经历过大的项目,所以不太能对里面的一些观点产生共鸣,不过里面还是有些较为简单的观点让我收获了许多. 1.&qu ...

最新文章

  1. 硬核!如何全面系统地自学 Java ?(必看)
  2. 开发工具之pycharm 快捷键说明
  3. rn webview加载本地静态html,RNwebview加载本地html.htm
  4. 【经验】对“面试造火箭,入职拧螺钉”的看法
  5. Mac VS code latex pdf 安装教程
  6. javascript美术馆
  7. 使用遇到的问题_使用三防整理剂做织物整理会遇到这些问题...
  8. VmPlayer上设置主机和windows虚拟机文件夹共享
  9. 使用Python实战反欺诈模型
  10. 红米手机使用应用沙盒一键修改cpu信息
  11. day01 -Web API介绍—DOM 介绍—获取元素—事件基础—操作元素—排他操作—自定义属性操作—节点操作—案例:动态生成表格—创建元素的三种方式(经典面试题)
  12. 跨考计算机日程记录01
  13. continue语句只用于循环语句中_Python尚学堂高淇|P60P63break语句,else语句使用,循环代码的优化...
  14. Rose Blumkin
  15. assaasasas
  16. 阶乘末尾 0 的个数.c
  17. Java筑基17-注解
  18. python证书微软认证_怎样考取微软工程师?
  19. SQL中EXTRACT() 函数
  20. iOS PerformSelector 遗漏问题

热门文章

  1. 微信小程序----模板(template)
  2. JS遍历数组,对象,字符串
  3. Pandas入门之常用函数介绍
  4. css中auto啥意思,css中auto什么意思?
  5. 望远镜和相机是如何工作的
  6. Linux下安装SAProuter
  7. 4G技术哪家强?说清楚FDD和TDD之间的事
  8. 使用js写一个播放语音提示的功能
  9. 融云发送图片消息_融云IM SDK集成,Server API 发送图片消息时,缩略图生成逻辑说...
  10. 国家高新技术企业认定国家高新技术企业复审指南