《代码大全》笔记 —— 模块一:奠定基础

软件构建

  • 软件构建是软件开发的核心活动;软件构建是每个项目唯一不可或缺的活动。
  • 软件构建主要包括详细设计、编码、调试、集成和开发者测试(单元测试和集成测试)活动。
  • 软件构建的其他常用术语是 “编码” “编程” 和 “程序设计”。
  • 软件构建活动的质量对软件质量有实质性的影响。
  • 个人对软件构建的理解程度,决定着一名程序员的优秀程度。

构建重要的原因:

  1. 构建活动是软件开发的主要组成部分
  2. 构建活动是软件开发中的核心活动
  3. 将精力集中于构建活动,可以显著提高程序员的生产力(Devops 的作用就是提高效率、专注)
  4. 构建活动的产物,即源代码,通常是对软件的唯一准确的描述
  5. 构建活动是唯一能确保开发完成的工作

通过隐喻地方式理解软件开发

  • 隐喻是启发式方法而不是算法,因而往往有些随意。
  • 隐喻把软件开发过程与人们熟知的活动联系在一起,帮助人们更好地理解。
  • 有些隐喻比其他一些隐喻更为贴切。
  • 通过把软件的构建过程比作房屋的建设过程,必须要精心准备,并且大型项目和小型项目也是有区别的。
  • 把软件开发的实践比作智慧工具箱中的工具,进一步表明每一个程序员都有许多工具,但并不存在任何一个能适合所有工作的工具,为每个问题选择合适的工具是成为高效程序员的关键。
  • 不同的隐喻并不排斥,要使用对自己最有用的隐喻组合。

谋定而后动:前期准备

  • 构建准备的首要目标是降低风险。确保准备活动可以减少而不是增加风险。
  • 开发高质量的软件,需要从开始到结束,对质量的关注必须是软件开发过程的一部分。项目初期关注质量,对产品质量的正面影响比在项目末尾关注质量的影响大。
  • 程序员的一部分工作是教育老板和合作者,告诉他们软件开发过程,包括在编程开始前做好充分准备的重要性。
  • 所从事的项目类型对构建活动的前期准备有很大影响,有些项目应该是高度迭代的,有些项目应该是序列式的。
  • 如果没有明确的问题定义,那么有可能会在构建期间解决错误的问题。
  • 如果没有做完良好的需求分析工作,可能没有察觉待解决问题的重要细节。如果是需求变更发生在构建之后的阶段,那么其代价是在项目早期更改需求的 20 到 100 倍。在开始编程之前需要确定需求已到位。
  • 如果没有完成良好的架构设计,那可能会在构建期间用错误的方法解决正确的问题。架构变更的代价随着为错误的架构编写代码数量增加而增加,因此也要确保架构已到位。
  • 要理解项目前期准备所采用的方法,并相应地选择构建方法。

前期准备的重要性

高质量的实践:实践在项目开始、中间和结束时始终都在强调质量。项目结束时候强调质量说明强调的是系统测试;开始时强调质量说明时在计划、要求并设计出高质量的产品;构建过程中至少应该能够确定具体情况并在面临失败的风险时做好备用方案。

准备工作的首要目标时降低风险:好的项目规划要尽早清除主要的风险,让项目的主体尽可能顺利的进行。

要抱着编程基本是项目的最后一步的心态。有效编程的关键思想之一是重视准备工作。发现错误的时间要尽可能接近于引入该错误的时间。

确定要开发什么类型的软件

错误的观点:使用迭代技术的项目根本不需要过多地关注前期准备工作。

对大多数项目来说,尽早把那些关键的需求要素和架构要素确定下来,是很有价值的。

一个常见的经验法则是,预先确定大约 80% 的需求,为以后再确定的额外需求预留时间,然后采用系统化的变更控制,以确保随着项目的进展只接受最有价值的新需求。另一种选择是预先确定最重要的 20% 需求,并计划以小的增量来开发软件饿其余部分,然后随着时间的推移来确定新增的需求和设计。

迭代式和序列式的选择方式:

  1. 序列式
    1. 需求相当稳定
    2. 设计很简单,夜很容易理解
    3. 开发团队熟悉应用程序领域
    4. 项目几乎没有风险
    5. 长期的可预测性很重要
    6. 后期变更需求、设计和代码的成本可能很昂贵
  2. 迭代式      1. 需求没有得到充分理解,或者出于其他的原因,而认为它们不稳定      2. 设计是复杂的、有挑战性的,或者两者兼而有之      3. 开发团队不熟悉应用程序领域      4. 项目有很多风险      5. 长期的可预测性并不重要      6. 后期变更需求、设计和代码的成本可能很低
    

对一个确定性很强的系统,预留某些迟早都要有的特性入口也是很重要的。

定义问题的先觉条件

问题定义只定义了问题是什么,并不涉及任何可能的解决方案。

问题定义要使用用户的语言,并且应该从用户的角度描述问题。通常不应该用计算机术语来描述。(目的是获取用户最原始的诉求,避免存在忽略点)

需求的先决条件

充分详尽地描述需求是项目成功的关键,甚至可能比有效的构建技术更重要。

稳定的需求是软件开发的圣杯。

确保每个人都知道需求变更的代价(成本的对齐、广播);建立一套变更控制程序(通过流程进行限制、规范)。

使用能适应变更的开发方法:演进原型法能够让人在投入全部精力构建系统之前,先探索系统的需求。

架构的先决条件

软件架构师软件设计的高层部分,是用于支撑更多细节的设计框架。架构是指适用于整个系统范围的设计约束,而高层设计是指适用于子系统层次或者多个类是层次上的设计约束(而不是真个系统范围的设计)。

不要轻易做架构变更,必须有充分充足的思考和理由。

典型的架构元素

  1. 程序的组织
    • 团队的组织影响架构,同样架构影响团队的组织
    • 架构应当定义系统主要的组件(高内聚、低耦合)
    • 明确定义各个组件的责任
    • 明确定义每个组件的通信原则
  2. 主要的类
    • 架构应当记述曾经考虑过的其他的类设计方案,并给出选择当前的组织结构的理由(对比优缺点)。
  3. 数据设计
    • 架构应当描述所用到的主要文件和数据表的设计
    • 架构应当详细定义所用数据库的高层组织结构和内容
  4. 业务规则
    • 如果架构依赖于特定的业务规则,就应该详细描述这些规则,并描述这些规则对系统设计的影响
  5. 用户界面设计
    • 用户界面常常在需求阶段进行详细说明
    • 架构应该模块化,以便在用户界面替换时不影响业务规则和程序的输出部分
  6. 资源管理
    • 架构应当描述一份管理稀缺资源的计划
  7. 安全性
    • 架构应当描述实现设计层面和代码层面安全性的方法
  8. 性能
    • 在需求中详细定义性能目标
  9. 可伸缩性
  10. 互操作性
  11. 国际化 / 本地化
    • 对交互系统,国际化问题值得在架构中关注
  12. 输入 / 输出
  13. 错误处理
  14. 容错性
    • 架构应当详细定义所期望的容错种类
  15. 架构可行性
  16. 过度工程
    • 通过在架构中明确设立期望目标,就能避免出现某些异类健壮,而其他类勉强健壮的情况
  17. 是购买还是构建,如何进行决策?
  18. 关于复用的决策
  19. 变更策略
  20. 架构的总体质量
    • 大型系统的本质问题是维持其概念的完整性
    • 好的解决方案是看上去既自然又容易的
    • 架构的目标应当阐述清楚。以系统可变形为首要目标的设计方案,肯定不同于性能第一的设计方案,即使两个系统的功能一样
    • 架构应当描述所有主要决策的目标(类似存储的目标为 “稳定高于一切”)
    • 架构应当明确地指出有风险的区域。应该解释为什么这个区域是有风险的,并说明已经采用了哪些步骤以使用风险最小化。
    • 架构应该包含多个视角

前期准备所花费的时间

一般来说,一个运行良好的项目会在需求、架构和其他前期准备方面投入 10%~20% 的工作量和 20%~30% 的时间。

如果软件是一种以前从未使用过的软件,那留出更多的时间来考虑在新领域中进行设计时候的不确定性。

关键的架构决策

  • 每种编程语言都有各自的优缺点。注意各种语言的优缺点。
  • 在开始编程之前建立编程约定。代码成形之后再适配约定几乎是不可能的。
  • 现有的构建实践比任何单个项目上使用的都多。用心选择最适合具体项目的实践。
  • 正在用的编程实践是对自己所用编程语言的正确响应,还是受它控制。要深入语言去学习编程,而不是浮于语言表面进行编程。
  • 在技术浪潮中处于哪个阶段决定着哪种方法是有效的或者是可能的。确定自己在技术浪潮中所处的阶段,并相应地调整计划和期望。

从效率上看,程序员使用熟悉的语言比不熟悉的语言更高。使用高级语言的程序员比使用低级语言的程序员能获得更高的生产力和质量。

深入语言去编程的程序员首先决定要表达什么思想,然后决定如何使用特定语言提供的工具来表达这些思想。