本书第一部分的内容,主要阐述了软件构建的含义及软件构建的前期准备工作,这些工作主要包括问题定义、需求分析、架构设计以及选择合适的语言和构建实践的方法。正如标题所说,这部分的内容为后面详述软件构建的各个方面打下了基础。

第1章 欢迎进入软件构建的世界

1、什么是软件构建,它包含哪些内容?

软件开发的过程,根据不同的活动内容可以划分为不同的阶段,它们分别是:问题定义需求分析规划构建软件架构/高层设计详细设计编码与调试单元测试集成测试集成系统测试保障维护

软件构建主要是指计算机软件开发过程中的编码与调试的过程,除此之外,还会涉及到详细设计、规划构建、单元测试、集成、集成测试等其他活动。

2、为什么软件构建十分重要?

软件构建是软件开发的主要组成部分,也是软件开发的核心活动,集中精力在构建活动可以大大提高生产率。对软件的唯一精确描述往往就是构建活动的产物:源代码。构建活动是唯一一项确保会完成的工作,构建活动的质量对软件的质量有着实质性的影响。

第2章 用隐喻来更充分的理解软件开发

1、隐喻是把双刃剑

通过形象的隐喻(比喻)描述软件领域中各种特定的想象和事物,使得我们能更深刻的理解软件开发的过程。通过将不太理解的东西和较为理解且十分相似的东西做比较的方法叫“建模”,通过类比建模,常常能产出重要的研发成果。

对隐喻的过度引申也会让人误入歧途。有的隐喻会比其他的隐喻更好,更能解释实验证据及其他已经观测到的现象。不同的隐喻彼此之间并不排斥,应当合理组合使用隐喻。

2、算法与启发(试探)法

算法是一套定义明确的指令,使你能完成某个特定的任务。算法是可预测的、确定性的、不易变化的。

启发(试探)法仅仅告诉我们如何去找,不会告诉我们要找什么,是一种帮助寻找答案的技术,得出的答案具有偶然性。

3、如何使用软件隐喻?

隐喻不是算法,而是一种启示和试探法,隐喻并没有一张详细的路线路,它可能就是一盏探照灯。应该用隐喻来提高对编程问题和编程过程的洞察力,用它来帮助思考编程过程中的活动,想象出更好的做事情的方法。

善于使用隐喻的人,对编程的理解会更好,能够更快地写出更好的代码。

4、常见的软件开发的隐喻

  • 软件中的书法:写作代码

    开发一个程序就像写一封有缘由的信一样,坐下来,从头写到尾就完成了。这样的隐喻适用于个人或者小型项目。

  • 软件的耕作法:培植系统

    创造软件类似于播种和耕种的情形,每次设计系统的一小部分、写出一段代码、做一点测试,将成果一点点的添加到系统中。这里的增量和生长的概念是有用的,但是很难将耕作恰当的引申到软件开发领域。

  • 软件的牡蛎养殖观点:系统生长

    牡蛎的生长是增量的、迭代的、自适应的、演进的,已增量的方式进行设计、编译和测试,是目前已知的最强有力的软件开发概念,增量式的开发的优势在于未做过度的承诺。

  • 软件构建:建造软件

    建造软件这一说法,暗示了软件开发中存在诸多阶段,如计划、准备及执行等。根据建造软件的不同,这些阶段的种类和程度可能会发生变化。建造房子的过程,可以对应到软件开发中的问题定义、架构设计、详细设计、软件构建、软件优化、软件评审和详查。

  • 应用软件技术:智慧工具箱

    编程方面的知识学得越多,工程师脑海中的工具箱会有更多的分析工具,也会知道在何时使用这些工具,以及正确的使用这些工具。工具箱隐喻能帮助把所有的方法、技术和技巧留在脑海,合适的时候拿来即用。

第3章 三思而后行:前期准备

1、为什么前期准备那么重要?

  • 能创造出高质量软件的程序员通常使用高质量的实践方法,在项目的初期、中期、末期都强调质量。项目末期的测试检查不出诸如“制造了一个错误的产品”或者“使用错误的方法制造的正确的产品”之类的缺陷,这些缺陷需要在构建活动之前解决。

  • 准备工作的中心目标是降低风险,软件开发中最常见的项目风险是糟糕的需求分析和糟糕的项目计划,准备工作的重点集中在改进需求分析和项目规划。

  • 前期准备往往是不周全的:分配去做前期准备活动的人员往往并不具备完成这一任务的专业技能;有一些程序员确实知道如何进行前期工作,但是他们却并没有做,因为他们不能够抵抗“尽快开始编码”的欲望;管理者们不理解、不支持花时间进行前期准备的程序员。

2、支持需要前期准备的论据

  • 诉诸逻辑:从逻辑上来讲,准备工作是做任何事情之前都需要做的事情。编码之前的准备工作,可以弄清楚要做的东西到底是什么,以免浪费时间和金钱,走进毫无必要的死胡同。

  • 诉诸类比:建造软件系统和任何其他花费人力财力的项目是相似的,如果在建造一座房屋前,不做任何准备和设计工作,直接丢给农民工去做,这样是造不出合格的房子的。

  • 数据说话:研究表明,在构建活动开始前清除一个错误,返工的成本仅仅是在软件开发的最后阶段做同样事情的十分之一到百分之一。发现错误的时间,要尽可能接近引入该错误的时间。缺陷修复的成本随着“从引入缺陷到检测到该缺陷之间的时间”变长而急剧增加。

3、根据软件类型决定前期准备工作

不同种类的软件项目,需要在“准备工作”和“构建活动”之间做出不同的平衡。开发商业系统的项目往往受益于高度迭代的开发法,这种方法的“计划、需求、架构”活动与“构建、系统测试、质量保证”活动交织在一起。性命攸关的系统往往要求采用更加序列式的方法,“需求稳定”是确保“超高等级可靠性”的必备条件之一。

4、迭代开发法与序列开发法的比较

  • 迭代方法往往能够减少“前期准备不足”造成的负面影响,但是他不能完全消除此影响。

  • 序列式开发法依赖测试发现缺陷,绝大部分缺陷修正工作推迟到项目快结束时进行,使得成本较高。迭代开发法在项目进行过程中一点点地吸收消化返工,使得总体成本较低。

  • 使用迭代开发法,成本将在整个项目过程中分次支付,而不会聚集到项目末尾一次性支付。项目结束,实际总成本是相似的,但是迭代开发的分期支付,使得成本看起来没那么高。

  • 无论使用迭代开发还是序列开发,只要进行前期准备,就可以减少成本。绝大多数的项目开发不会完全选择序列化开发或者是迭代开发。

5、迭代开发法与序列开发法的选择

  • 下列原因会让你选择更加序列化的方法

    • 需求相对稳定
    • 设计直截了当、理解透彻
    • 开发团队对这一应用领域非常熟悉
    • 项目的风险很小
    • “长期可预测性”很重要
    • 后期改变需求、设计、和编码的代价可能较昂贵
  • 下列原因会让你选择更加迭代的方法

    • 需求并没有理解透彻,或者出于其他理由,认为它是不稳定的
    • 设计很复杂或者有挑战性,或者两者都有
    • 开发团队对这一应用领域不熟悉
    • 项目包含许多风险
    • “长期可预测性”不重要
    • 后期改变需求、设计、和编码的代价很可能较低

事实上,软件开发中,适用迭代开发法的情况比适用序列式开发法的情况多的多。

6、什么是问题定义

问题定义是指在开始构建前,对系统要解决的问题做出清楚的陈述。问题定义只定义了问题是什么,而不涉及任何可能的解决方案。问题定义为随后的开发过程打下基础,应该用客户的语言来书写,从客户的角度来描述问题。 如果解决的是计算机本身相关的问题,则使用计算机术语陈述问题是恰当的。

7、关于需求

需求详细描述软件系统应该做什么,这是达成解决方案的第一步。

明确的需求有助于确保是用户(而不是程序员)驾驭系统的功能、避免争论。重视需求有助于减少开始编程开发之后的系统变更。充分详尽的描述需求,是项目成功的关键。没有好的需求,可能对问题有整体把握,但是却没有击中问题的特定方面。

稳定点需求是软件开发的圣杯。一旦需求稳定,项目就能有序、可预测、平稳的完成从架构到设计到编码到测试等一系列工作。但是永不变更的需求只是一个美好的愿望。需求变更的主要来源是,随着项目的推进,客户对项目的理解也越深入。

8、构建期间,最好的处理需求变更的方式:

  • 评估需求的质量
  • 确保每一个人都知道需求变更的代价
  • 建立一套变更控制程序
  • 使用能适应变更的开发方法
  • 放弃这个项目
  • 注意项目的商业案例

[需求核对表]

9、关于架构

软件架构师软件设计的高层部门,是用于支撑更细节的设计的框架。架构的质量决定了系统的“概念完整性”,继而决定了系统的最终质量。架构将工作分为几部分,使得多个开发者或者多个开发团队可以独立工作。

没有良好的软件架构,可能瞄准了正确的问题,但是却使用了错误的解决方案,也许完全不可能有成功的构建。

架构变更如同需求变更,对软件构建影响很大,越早发现架构缺陷,修正成本越低。

10、架构的典型组成部分

程序组织、主要的类、数据设计、业务规则、用户界面设计、资源管理、安全性、性能、可伸缩性、互用性、国际化/本地化、输入输出、错误处理、容错性、架构可行性、过度工程、关于买还是造的决策、关于复用的决策、变更策略、架构的整体质量。

[架构核对表]

11、前期准备的时间长度

花费在问题定义、需求分析、软件架构上的时间,根据项目的需要而变化。运作良好的项目会在前期工作上投入10%~20%的工作量和20%~30%的时间。

如果需求不稳定,则需要预留更多的时间给需求分析师修订需求,或者自己解决需求方面的问题。

[前期准备核对表]

第4章 关键的“构建”决策

1、选择合适的编程语言

编程语言的选择从多个方面影响生产率和代码质量。使用熟悉的语言比使用不熟悉的语言生产率要高。使用高级语言比使用较低级语言能达到更好的生产率和质量。

高级语言比低级语言在生产率、可靠性、简洁性、易理解性等方面高5到15倍,高级语言比低级语言的表达力更强,每行代码能表达更多的含义。某些语言更能表达编程中的各种概念。

编程语言影响程序员的思维。对于不熟悉的语言,虽然能写出可以编译通过的代码,但是往往采用的是熟悉语言的思维方式,忽略不熟悉语言的的高级特性。

2、编程约定

针对变量名称、类的名称、子程序名称、格式约定、注释约定等方面的编程约定,将使得编程实现与架构保持一致。

编程约定使得程序的结构平衡,统一编程语言的细节,使得各个部件都能反映整体架构的内涵。没有统一的规则,创作出来的东西充斥着各种不同的风格,混乱而邋遢,把握这些不同风格,给工程师的大脑带来沉重负担。

编程约定的细节要达到编码完成后几乎不可能改变所遵循的编程约定的精度。

3、明确自己在技术浪潮中的位置、深入一种语言编程

信息技术日新月异,不同时期的技术环境完全不同。在成熟时期编码会更容易。但是即使在技术浪潮的初期或者是某个新技术的早期,决定项目成败的更多是程序员的编程思路。

大多数的编程原则并不依赖特定的语言,而依赖于你使用语言的方式。如果你使用的语言缺乏你希望用的构件,应该试着去弥补它,发明自己的编码约定、标准、类库以及其他改进措施。

4、选择主要的构建实践方法

构建的实践方法的种类比任何单个项目中用到的要多,需要有意识的选择最适合项目的时间方法;如某些项目使用结对编程和测试驱动开发,而其他项目使用单人开发和形式化检查。

[主要的构建实践核对表]

更多有关《代码大全 2》的读书笔记,请关注 :
http://tabalt.net/blog/code-complete-2-reading-notes/

本文链接:http://tabalt.net/blog/cc2-laying-the-foundation/,转载请注明。