《程序员修炼之道》——第二章 注重实效的途径(一)

七、重复的危害
  我们觉得,可靠地开发软件、并让我们的开发更易于理解和维护的唯一途径,是遵循我们称之为DRY的原则:

  系统中的每一项知识都必须具有单一、无歧义、权威的表示。

    DRY-Don‘t Repeat Yourself.

      不要重复你自己。

  与此不同的做法是在两个或更多的地方表达同一事物。如果你改变其中一处,你必须记得改变其他各处。或者,就像那些异型计算机,你的程序将因为自相矛盾而被迫屈服。这不是你是否能记住的问题,而是你何时忘记的问题。

 

  重复是怎样发生的

  我们所见到的大多数重复都可归入下列范畴:

  • 强加的重复(imposed duplication)。开发者觉得他们无可选择——环境似乎要求重复
  • 无意的重复(inadvertent duplication)。开发者没有意识到他们在重复信息。
  • 无耐性的重复(inpatient duplication)。开发者偷懒,他们重复,因为那样似乎更不容易。
  • 开发者之间的重复(interdeveloper duplication)。同一团队(或不同团队)的几个人重复了同样的信息。

  强加的重复

  有时,重复似乎是强加给我们的。项目标准可能要求建立含有重复信息的文档,或是重复代码中的信息的文档。多个目标平台各自需要自己的编程语言、库以及开发环境,这会使我们重复共有的定义和过程。编程语言自身要求某些重复信息的结构。我们都在我们觉得无力避免重复的情形下工作过。然而也有一些方法,可用于把一项知识存放在一处,以遵守DRY原则,同时也让我们的生活变得更容易一点。

  信息的多种表示。在编码一级,我们常常需要以不同的形式表示同一信息。我们也许在编写服务器应用,在客户和服务器端使用了不同的语言,并且需要在两端都表示某种共有的结构。我们或许需要一个类,其属性是某个数据库表的schema(模型、方案)的镜像。你也许在描写一本书,其中包括的程序片段,也正是你要编译并测试的程序。

  发挥一点聪明才智,你通常能够消除重复的需要。答案常常是编写简单的过滤器或代码生成器。可以在每次构建软件时,使用简单的代码生成器,根据公共的元数据表示构建多种语言下的结构。可以根据在线数据库schema、或是最初用于构建schema的元数据,自动生成类定义。

  代码中的文档。程序员被教导说,要给代码加上注释;好代码有许多注释。遗憾的是,没有人教他们,代码为什么需要注释;糟糕的代码才需要许多注释。

  DRY法则告诉我们,要把低级的知识放在代码中,它属于那里;把注释留给其他的高级说明。否则,我们就是在重复知识,而每一次改变都意味着既要改变代码,也要改变注释。注释将不可避免地变得过时,而不可信任的注释完全没有注释更糟。

  文档与代码。你撰写文档,然后编写代码。有些东西变了,你修订文档、更新代码。文档和代码都含有同一知识的表示。而我们都知道,在最紧张的时候——最后期限在逼近,最重要的客户在喊叫——我们往往会推迟文档的更新。

  语言问题。许多语言会在源码中强加可观的重复。如果语言使模块的接口与实现分离,就常常会出现这样的情况。C和C++有头文件,在其中重复了被导出变量、函数和(C++)类的名称和信息。Object Pascel甚至会在同一文件里重复这些信息。如果你使用远地过程调用或CORBA,你将会在接口规范与实现它的代码之间重复接口信息。

  

  无意的重复

  有时,重复来自设计中的错误。

  让我们看一个来自配送行业的例子。假定我们的分析揭示,一辆卡车有车型、牌照号、司机及其他一些属性。与此类似,发运路线的属性包括路线、卡车和司机。基于这一理解,我们编写了一些类。

  但如果Sally打电话请病假、我们必须改换司机,事情又会怎么样呢?Truck和DeliverRoute都包含有司机。我们改变哪一个?显然这样的重复很糟糕。根据底层的商业模型对其进行规范化——卡车底层的属性集真的应包含司机?路线呢?又或许我们需要第三种对象,把司机、卡车及路线结合在一起。不管最终解决方案是什么我们,我们都应避免这种不规范的数据。

  当我们拥有多个互相依赖的数据元素时,会出现一种不那么显而易见的不规范数据。让我们看一个表示线段的类:

1 class Line{
2     public:
3      Point start;
4      Point end;
5      double length;
6 };

  第一眼看上去,这个类总是合理的。线段显然有起点和终点,并总是有长度(即使长度为零)。但这里有重复。长度是由起点和终点决定的:改变其中一个,长度就会变化。最好是让长度成为计算字段:

class line{
    public:
     Point start;
     Point end;
     double length() {return start.distanceTo(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 = start.distanceTo(end);
            changed = false;
        }
        return length;
    }
};

这个例子还说明了像Java和C++这样的面向对象语言的一个重要问题。在可能的情况下,应该总是用访问器(accessor)函数读写对象的属性。这将使未来增加功能(比如缓存)变得更容易。

  无耐性的重复

  每个项目都有时间压力——这能够驱使我们中间最优秀的人走捷径的力量。需要你与写过的一个例程相似的例程?你会受到诱惑,去拷贝原来的代码,并做出一些改动。需要一个表示最大点数的值?如果我改动头文件,整个项目就得重新构建。也许我应该在这里使用直接的数字,这里,还有这里,需要一个与Java Runtime中的某个类相似的类?源码在那里(你有使用许可),那么为什么不拷贝它、并作出你所需的改动呢?

  如果你觉得受到诱惑,想一想古老的格言:“欲速则不达”。你现在也许可以节省几秒钟,但以后却可能损失几小时。、

  无耐性的重复是一种容易检测和处理的重复形式,但那需要你接受训练,并愿意为避免以后的痛苦而预先花一些时间。

  开发者之间的重复

  另一方面,或许是最难检测和处理的重复发生在项目的不同开发者之间。整个功能集都可能在无意中被重复,而这些重复可能几年里都不会被发现,从而导致各种维护问题。

  在高层,可以通过清晰的设计、强有力的技术项目领导,以及在设计中进行得到了充分理解的责任划分,对这个问题加以处理。但是,在模块层,问题更加隐蔽。不能划入某个明显的责任区域的常用功能和数据可能会被实现许多次。

  我们觉得,处理这个问题的最佳方式是鼓励开发者相互进行主动的交流。设置论坛,用以讨论常见问题。让某个团队成员担任项目资料管理员,其工作是促进知识的交流。在源码树种指定一个中央区域,用于存放实用例程和脚本。一定要阅读他人的源码与文档,不管是非正式的,还是进行代码复查。你不是在窥探——你是在向他们学习。而且要记住,访问时互惠的——不要因为别人钻研你的代码而苦恼

  

  让复用变得容易

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

时间: 2024-11-04 22:23:35

《程序员修炼之道》——第二章 注重实效的途径(一)的相关文章

《程序员修炼之道》之注重实效

十月这一个月以来,读了关于程序员修炼之道的第二站,注重实效,其中有一句话让我印象深刻. 系统中的每一个知识都必须具有单一,无歧义,权威的表示. 通过这本书,我了解到我们程序员对我们所创建的应用进行维护时,我们必须找到并改变事情的表示,在我们开发的规范,过程和程序中很容易重复和表达知识,然而,这样会很容易让我们的代码失效,并且通过dry我了解到了,它不仅仅存在于我们的程序,更存在于我们的生活,我们在编写代码的时候,不是所有代码都需要加注释的,也不是所有的代码都不加注释,而是择优,选择你认为的高级代

程序员修炼之道第一章读后感

首先我读了序言,明白了这本书可能现在还是读不懂,但是书中的有些知识可能会帮助我以后开发软件少走点弯路, 所以,可能我现在还不太懂,但是我一定会好好阅读,尽量多理解书中的一些经验和内容,这是我对自己读这本书的要求. 求. 接着我读了第一章的内容,虽然第一章篇幅不多,但整体读下来还是收获很多的.在第一章的一开始,就教会了我做一 个做一个程序员的原则,那就是诚实和负责任.如果你做错了某些事,承认它,并给出补救的选择,不要把责任推卸给别人. 更不要找各种各样的借口.不要说事情做不到,而要说能够做什么来挽

程序员修炼之道——第一章读后感

这两天读了程序员的修炼之道第一章,感觉收获很多. 注重实效的编程源于注重实效的思考的哲学. 注重实效的程序员的特征:他们处理问题的能力,寻求解决问题的风格.态度.哲学.他们能直接超越问题本身去思考, 而我们却不能,这是值得我们思考的,总是把问题放在大语境中,,总是设法引起更大的图景,,总是设法注意更大的图景, 没有个更大的语境,就没办法注重实效,就没办法做纯更大的妥协,更长远的展望. 程序员成功的原因之一是他们对每件事情都很负责,他们不会看着项目土崩瓦.注重时效的程序员对自己的职业生涯负责,并且

读书笔记2014第4本:程序员修炼之道-从小工到专家(第七、八章)

第七章 在项目开始之前 36 需求之坑不为收集需求,挖掘它们.有一种能深入了解用户需求,却未得到足够利用的技术:成为用户.与用户一同工作,以像用户一样思考.描述需求文档时,要使用项目术语表.用WEB来收集和管理需求. 37 解开不可能解开的谜题遇到不可能解决的问题时,退一步问问自己如下问题:1)有更容易的方法吗?2)你是在设法解决真正的问题,还是被外围的技术问题转移了注意力?3)这件事情为什么是一个问题?4)是什么使它如此难以解决?5)它必须以这种方式完成吗?6)它真的必须完成吗? 38 等你准

第4本:程序员修炼之道-从小工到专家(第七、八章)

第4本:程序员修炼之道-从小工到专家(第七.八章) 第七章 在项目开始之前 36 需求之坑不为收集需求,挖掘它们.有一种能深入了解用户需求,却未得到足够利用的技术:成为用户.与用户一同工作,以像用户一样思考.描述需求文档时,要使用项目术语表.用WEB来收集和管理需求. 37 解开不可能解开的谜题遇到不可能解决的问题时,退一步问问自己如下问题:1)有更容易的方法吗?2)你是在设法解决真正的问题,还是被外围的技术问题转移了注意力?3)这件事情为什么是一个问题?4)是什么使它如此难以解决?5)它必须以

程序员修炼之道-书评

-------------------------------------------------------------------------------------------------------- 第一章     注重实效的哲学: -------------------------------------------------------------------------------------------------------- 1.我的源码让猫给吃了 启发:要为自己负责 提

《程序员修炼之道》笔记(二)

第二章 注重实效的途径 1. 重复的危害 a) DRY-Don't Repeat Yourself.系统中的每一项知识都必须具有单一.无歧义.权威的表示. b) 重复是怎样发生的 Imposed Duplication强加的重复.开发者觉得他们无可选择-环境似乎要求重复. Inadvertent Duplication无意的重复.开发者没有意识到自己在重复信息. Impatient Duplication无耐心的重复.开发者偷懒,因为重复似乎更容易. Interdeveloper dumplic

《程序员修炼之道---从小工到专家》第一章读后感

<程序员修炼之道---从小工到专家>一书由美国Andrew Hunt和David Thomas所著,主要讲述了一位程序员应当如何从个人责任,职业发展,到基本工具,实际的编程项目中发展自己.本书第一章的标题为:注重实效的哲学. 第一章整体主要从程序员个人对待团队,同事,领导,以及自己的前途发展,学习时应当持有的态度出发入手,讲述了程序员在公司中应有的职业操守和准则.作者在开篇时向我们讲述了应当如何成为一位高效的程序员,而后则是分别从各各的方面论述这一观点.首先 第一点是:要对事负责.在工作中如果

《程序员修炼之道》读后感(一)【序言与第一章】

关于本书 在真正打开本书之前我只听说过<程序员修炼之道>是一本好书,我以为它和老师之前介绍过的<大道至简>一样是一本讲精义.讲道理的书(并不是说这样的书不好,只是它们对于开发经验欠缺的学生来说实在有些遥远与晦涩),而本书开篇寥寥几句便打破了我的认识.虽然我只读了本书的前半部分,但从这冰山一角中我可以窥见本书的真诚与实用,我佩服作者对于简单句的运用,第一次打开本书序言的第一句话“本书将帮助你成为更好的程序员”使本来昏昏欲睡的我打了个激灵,我看过别的技术书籍往往开篇会说本书会讲什么.你