代码大全-读书笔记

一、打好基础

1、人生苦短,当有大量更好到选择摆在你面前的时候,在一个蛮荒的软件企业中工作是不明智的。  --3.1 前期准备的重要性

2、作为技术雇员,你的一部分工作就是培训周围的非技术人员,讲解开发过程。

3、程序员是软件食物链的最后一环。架构师吃掉需求,设计师吃掉架构,而程序员则消化设计。

4、发现错误的时间要尽可能接近引入该错误的时间。缺陷在软件食物链里面呆的时间越长,它对食物链的后极产生的影响就越大。

5、修复缺陷的成本随着“从引入缺陷到检测该缺陷之间的时间“,变长而急剧增加。无论项目是高度序列化还是高度迭代化都成立。

6、一条很有用的经验规则是,计划好预先对大约80%的需求做出详细说明,并给“稍后再进行详细说明的额外需求”分配一定的时间。然后在项目进行过程中,实施系统化的变更控制措施--只接受那些最有价值的需求。另外一种方案就是现对20%最重要的需求做详细说明,然后计划以小幅增量开发软件的剩余部分。

3.4 需求的先决条件

  1)确保每个人都知道需求变更的代价。当客户提出需求变更时,让他们明白“进度”与“成本“,这两个字相当提神,许多”必须要有“很快变成”有就最好“。

  2)注意项目的商业价值。有些需求作为功能特色来看是不错的想法,但是当你评估”增加的商业价值“时就会觉得它是个糟透的主意。那些记得”考虑自己的决策所带来的商业影响“的程序员身价与黄金相当。

3.5 架构的先决条件

  程序组织部分:

  1)系统架构首先要以概述的形式对有关系统做一个综述。

  2)在架构中,应该能发现对那些曾经考虑过的最终组织结构的替代方案的记述,找到之所以选用最终的组织结构,而不用其他替代方案的理由。

  3)架构应该定义程序的主要构件块,及各个构件块的责任。明确定义各个构件块的通讯规则,每个构件块直接使用的块,间接可使用的块,不可使用的块。

  4)架构应该详细定义所使用的主要的类(比如完成主要功能80%的那20%的类)。

  变更策略:

  架构的总体质量:

  1)架构应该描述所有主要决策的动机,谨防“我们向来这样做”这种自认为有理的说法。

  2)架构赢明确指出有风险的区域,并且采取的措施使风险最小化。

  3)架构应该包含多个视角(特别注意部署视图)。

4.1 选择编程语言

  语言学家Sapir和Whorf对“语言的表达能力”和“思考的能力”之间的关系提出了一个假说:你思考的能力,取决于你是否知道能够表达该思想的词汇。如果你不知道这些词汇,就无法表达出这种思想,甚至可能不能形成这种思想。

  “在一种语言上编程”与“深入一种语言去编程的区别”。如果你使用的语言缺乏你希望用的构件,或者倾向于出现其他种类的问题,那就应该试着去弥补它,发明你自己的编码约定,标准,类库以及其他改进措施。

 二、设计

5.1 设计中的挑战

  1)“险恶的”问题就是那些只有通过解决或部分解决才能被明确的问题。也就是说只有做过一遍才知道。

  2)因为设计永无止境,因此对上述问题(何时算是足够好等)最常见的回答是“到你没时间在做了为止”。

  3)设计的要点,一部分是在创造可能发生的事情,另一部分又是在限制可能发生的事情。

  4)设计过程充满了不确定性,因此设计技术也就趋于具有探索性--“经验法则”或者“实施没准能行”--而不是保证能产生预期结果的可重复过程。

5.2 设计的关键概念

  1)管理复杂度是软件开发中最为重要的技术话题。

  2)对于软件开发人员,我们不应该试着在同一时间把整个程序都塞进自己的大脑,而应该试着以某种方式去组织程序,以便能够在一个时刻可以专注于一个特定的部分。这么做的目的是尽量减少在任一时间所要考虑的程序量。你可以把它想成是一种心理上的杂耍(边抛边接)程序要求你在空中保持的球越多,你就越可能漏掉其中的某一个,从而导致设计或编码错误。

  3)在软件架构层次,可以通过把整个系统分解为多个子系统来降低问题的复杂度。子系统之间的相互依赖越少,那就越容易在同一时间里专注问题的一小部分。保持子程序的短小精悍也能帮助你较少思考的负担。从问题的领域着手而不是从底层实现细节入手去编写程序,在最抽象的层次上工作,也能较少人的脑力负担。

  4)一旦你能理解软件开发中任何其他技术目标都不如管理复杂度重要时,从多设计上的考虑就变得直截了当了。

  5)理想的设计特征:最小复杂度、易于维护、松散耦合、可扩展性、可重用性、高扇入(大量的类使用某个给定的类)、低扇出(一个类里少量或适中地使用其他的类)、可移植性、精简性(问一个关键问题:着虽然简单,但把它加进来之后会损害什么呢“)

  层次性:层次性意味着尽量保持系统各个分解层的层次性,使你能在任意层面上观察系统,并得到某种具有一致性的看法。设计出来的系统应该能够在任意层次上观察而不需要进入其他层次。

  层次如下:软件系统 -》子系统或包-》包中的类-》类中的数据与子程序-》子程序内部。

  子系统:特别注意不同子系统之间相互通信的规则。如果所有子系统都能通其他子系统通信,就完全失去了把他们分开所带来的好处。应该通过限制子系统之间的通信来让每个子系统更有存在的意义。

  常见的子系统:数据库访问(将数据库访问隐藏起来),对系统依赖的子系统(将所有的系统调用隔离起来);隐藏实现细节的子系统可以为系统提供有价值的抽象层,从而减少程序的复杂度。

  包中的类:该层的主要涉及任务是把所有的子系统进行适当分解,并确保分解出的细节都恰到好处,能够使用单个类实现。

  类中的数据与子程序:完成的定义出类内部的子程序,常常会有助于更好的理解类的接口,反过来这也有助于对类的接口进行进一步的修改,也就是在此返回到《包中的类》层。

5.3 设计构造块:启发式方法

  抽象是一种能让你在关注某一概念的同时可以放心地忽略其中一些细节的能力--在不同的层次上处理不同的细节。

  从复杂度的观点:抽象的主要好处就在于它能使你忽略无关的细节。

  优秀的程序员会在子程序接口的层次上,在类接口的层次上以及包接口的层次上进行抽象。

  面向对象设计与信息隐藏之间的差异,要比规章与临时制度之间的差异还要微妙。按照信息隐藏的原则来思考,能够激发和促进某些设计决策的形成,而仅仅按照对象原则思考则不会。请养成为“我该隐藏些什么”的习惯,你会惊奇地发现,有很多很棘手的设计难题都会在你面前化解。

   耦合标准:规模(模块之间的连接数)、可见性(模块之间连接的显著程度)、灵活性(连接是否容易改动)。在创建系统架构时,请按照“尽可能缩减相互连接”的准则来分解程序。如果把程序看作是一块木材,那么请沿着木材的纹理把它劈开。

  耦合种类:

    简单数据参数耦合(模块之间通过简单数据类型参数来传递数据)

    简单对象耦合(模块实例化另一个模块中的对象,通过该对象实现连接)

    对象参数耦合(两个模块之间通过第三个模块对象进行连接)

    语义耦合(一个模块不仅使用了另一个模块的语法元素,还是用了有关那个模块内部工作细节的语义知识--危险

    高耦合导致模块失去了抽象能力,模块所具有的的管理复杂度的能力也削弱或完全丧失了。类和子程序适用于降低复杂度的首选和最重要的智力工具。如果它没帮助你简化工作,那么他们就是失职的。

三、设计实践

1、设计是一种迭代过程。你并非只能从A点到B点,而是可以从A点到B点,再从B点返回到A点。

2、当你在备选的设计方案之中循环并且尝试一些不同的做法时,你将同时从高层和低层的不同视角去审视问题。这种高低层之间的互动被认为是一种良性的原动力,他所创建的结构要远远稳定与单纯自上而下或者自下而上创建的结构。

3、建立实验性原型方法:写出用于回答特定设计问题的,量最少且能够随时丢掉的代码

4、如果在编码之前我还判断不了应该在做多深入的设计,那么我宁愿去做更详细的设计。最大的设计失误来自于误以为自己已经做得很充分,可事后却发现还是做得不够,没能发现其他一些设计挑战。

5、我几乎没有遇到过因为做了太多设计而受损害的项目。另一方面我偶尔看到一些项目因为太过于专注对设计进行文档化而导致失败。

6、请把设计看成是一个险恶的,杂乱的和启发式的过程。不要停留于你所想到的第一套解决方案,而是去寻求合作,探求简洁性,在需要的时候做出原型,迭代,并进一步迭代。

四、可以工作的类

1、成为高效程序员的一个关键就在于,当你开发程序任一部分的代码时,都能够安全的忽视程序中尽可能多的其余部分。

2、创建高质量的类,第一步,可能也是最重要的一步,就是创建一个好的接口。这也包括创建一个可以通过接口来展现的合理的抽象,并确保细节仍被隐藏再抽象背后。类的接口为隐藏再其后的具体实现提供了一种抽象。

3、良好的类接口

  1)类的接口应该展现一致的抽象层次。把类看作一种用来实现抽象数据类型(ADT)的机制。每一个类应该实现一个ADT,并且仅实现这个ADT。(也就是避免其中几个接口在实现ADT1,另外几个实现ADT2,这就导致抽象层次不一致)。

  2)尽可能让接口可编程,而不是表达语义。可编程的部分由接口的数据类型于其他属性构成,编译器能够检查。而语义只能通过注释表达(比如要先初始化什么之类)。

五、高质量的子程序

1、在子程序上设计

内聚性,对于子程序而言是指子程序中各种操作之间联系的紧密程度。

功能的内聚性,就是让一个子程序仅执行一项操作。最好的内聚性,其他类型的内聚性通常都是不好的。

顺序上的内聚性:子程序内包含有需要特定顺序执行的操作,这些步骤需要共享数据,而且只有在全部执行完毕后才完成一项完整功能。

通信上的内聚性:子程序内部的不同操作使用了同样的数据,但不存在其他任何联系。(为了减少通讯而将不同的操作放到一起)。

临时内聚性:含有一些因为同时执行才放到一起的操作的子程序。比如在startup子程序中塞进一堆互不相关的代码。解决方法:可以把临时性的子程序看作是一些列事件的组织者。

逻辑上的内聚性:子程序的控制流或者所谓的“逻辑”是将这些操作放到一起的唯一原因。

 六、防御式编程

  防御式编程来自于防御式驾驶,那就是你永远也不能确定另一位司机将要做什么。这样才能确保在其他人做出危险动作时你不会受到伤害。

1、断言的指导建议

  1、用错误处理代码来处理预期发生的状况,用断言来处理绝不应该发生的状况。

  2、用断言来注解并验证前条件与后条件。

2、隔离程序

  1、把某些接口选定为“安全”区域的边界。对穿越安全边界的数据进行合法性检验,并当数据非法时做出敏锐的反应。让软件的某些部分处理“不干净”的数据,而让另一部分处理“干净”的数据,即可让大部分代码无须再负担检查错误数据的职责。

  在类层次上也可以使用该方法,类的公用方法可以假设数据是不安全的,而私有方法则可以假设数据是安全的。

3、隔离程序于断言相结合

  也就是在安全边界之外使用错误处理方法,而在安全;边界之内则可以使用断言。

 

 
 
 
 
 
 

 

 
 
 

 

 
 
 

 

posted @ 2013-05-21 09:32  chang290  阅读(220)  评论(0编辑  收藏  举报