💡
  • 构造包含的活动
  • 名词解释
    • 重构、测试驱动开发、结对编程
  • 给定代码段示例,对其进⾏改进或者 发现其中的问题
    • 简洁性/可维护性
    • 使⽤数据结构消减复杂判定
    • 控制结构
    • 变量使⽤
    • 语句处理
    • How to write unmaintainable code
    • 防御与错误处理
  • 单元测试⽤例的设计
  • 契约式设计
  • 防御式编程
  • 表驱动

构造包含的活动

  1. 详细设计: 构造阶段不可避免涉及详细设计的调整工作, 因为编程语言是软件设计的一个重要约束, 人们可能发现与预想不一样的情况和更多的约束, 需要修改详细设计方案. 方法和技术与设计阶段相同.
  1. 编程: 软件构造的核心活动, 目的是生产高质量的程序代码.
  1. 测试: 单元测试, 集成测试.
  1. 调试: 重现问题, 诊断缺陷, 修复缺陷.
  1. 代码评审: 正式评审, 轻量级评审, 结对编程.
  1. 集成与构建: 将分散的程序基本单位集成和构建为构件, 子系统和完整系统. 构建将可读的源代码转换为可执行文件, 需要配置管理工具帮助.
  1. 构造管理: 包括构造计划 , 度量, 配置管理.

构造实践方法

重构

定义: 修改软件系统的严谨方法, 在不改变代码外部表现(软件系统功能)的情况下改进其内部结构(详细设计结构的质量).
时机: 新功能增加完成之后, 发现缺陷并进行修复时, 进行代码评审时.
重构是基于已有代码的设计改进, 不能开发新代码. 要防止副作用, 不能改变外部行为或引入错误. 重点是改进详细设计结构, 体系结构设计不要轻易修改.

测试驱动开发

又称测试优先的开发.
测试驱动开发要求程序员在编写⼀段代码之前,优先完成该段代码的测试代码。完成测试代码之后,程序员再编写程序代码,并在编程中重复执⾏测试代码,以验证程序代码的正确性。
优点: 提高程序的可靠性和正确性, 提高设计质量, 提高生产力.
测试驱动开发过程
测试驱动开发过程

结对编程

两个程序员(驾驶员, 观察员)挨着坐在⼀起,共同协作进⾏软件构造活动.
驾驶员负责输入代码, 观察员负责对驾驶员输入的代码进行评审, 发现拼写错误和语法错误, 考虑战略性方向, 提出改进意见或将来可能出现的问题, 以便处理.

契约式设计

又称断⾔式设计,它的基本思想是:如果⼀个函数或⽅法,在前置条件满⾜的情况下开始执⾏,完成后能够满⾜后置条件,那么这个函数就是正确、可靠的。
契约式设计的两种常见编程方式:
  1. 异常: 在代码开始执行时检查前置条件是否满足, 在代码执行完之后再检查后置条件是否满足, 如果不满足就立刻抛出异常.
  1. 断言: 方便实现契约式设计而提供的机制, 如果断言表达式计算结果为假则断言抛出AssertionError异常.
异常的种类更多且可以自定义, 比断言灵活很多.

防御式编程

基本思想:在⼀个⽅法与其他⽅法、操作系统、硬件等外界环境交互时,不能确保外界都是正确的,所以要在外界发⽣错误时,保护⽅法内部不受损害。
与契约式设计有共同点,又有很⼤的差异。
共同点是他们都要检查输⼊参数的有效性, 差异性是防御式编程将所有与外界的交互(而不只是前置条件)都纳⼊防御范围, 不检查输出和后置条件, 由使用者自行检查.
实现方式: 异常与断言.

表驱动

对于特别复杂的决策,可以将其包装为决策表,然后⽤使⽤表驱动编程的⽅式加以解决. 保持表的结构不变, 每次修改决策细则时只需要修改表项的数据即可.
💡
“表驱动”是一种编程范式或设计模式,它通过查阅数据表来代替复杂的条件逻辑(如大量的if-elseswitch-case语句),从而实现程序的控制流或数据处理。
核心思想
其核心思想是将程序的行为或数据映射到一个数据结构(通常是数组、哈希表、字典或更复杂的查找表)中。当程序需要根据特定条件执行不同操作或获取不同数据时,它不是通过一系列判断来决定,而是通过查找这个数据结构来获取相应的信息或执行相应的操作。
构成要素
  1. 数据表 (The Table)
      • 这是表驱动方法的关键。它存储了条件与操作或数据之间的映射关系。
      • 表的结构可以很简单,比如一个包含键值对的数组;也可以很复杂,包含多维数据、函数指针、或表示状态转换的信息。
      • 表中的数据可以是硬编码在代码中,也可以从外部文件(如配置文件、数据库)加载。
  1. 查找逻辑 (The Look-up Logic)
      • 负责根据输入条件,在数据表中查找对应的项。
      • 这通常比复杂的if-elseswitch-case结构更简洁和高效。
  1. 操作/数据 (The Action/Data)
      • 从表中获取到的结果,可以是需要执行的函数、特定的数据值、状态码、或者其他任何能指导程序行为的信息。
优点
  • 代码简洁性:消除了大量重复的条件判断语句,使代码更短、更易读。
  • 可维护性:当需要添加新的条件或修改现有行为时,通常只需要修改数据表,而无需改动核心逻辑代码。这大大降低了维护成本和引入错误的风险。
  • 可扩展性:新的功能或规则可以很容易地通过向表中添加新的条目来实现,而不需要修改已有的代码结构。
  • 性能提升:在某些情况下,特别是当条件分支非常多时,查表操作可能比一系列连续的条件判断更快。
  • 灵活性:表中的数据可以动态加载或配置,使得程序的行为可以在运行时进行调整,而无需重新编译。
  • 消除重复代码:避免了在多个if-elseswitch-case分支中重复实现相似的逻辑。
常见应用场景
  • 状态机 (State Machines):用表格来定义状态之间的转换规则和每个状态对应的操作。
  • 命令分发 (Command Dispatching):将用户输入或事件映射到相应的处理函数。例如,一个命令解释器可以有一个命令名称到函数指针的映射表。
  • 规则引擎 (Rule Engines):将业务规则定义在表中,程序根据输入数据查表来决定如何执行。
  • 配置管理 (Configuration Management):将配置信息存储在表中,程序启动时加载并根据配置调整行为。
  • 策略选择 (Strategy Selection):类似于策略模式,但通过查表来动态选择具体的策略实现。
  • 查找数据:例如,根据错误码查找对应的错误消息,或者根据产品ID查找产品属性。
示例
传统方式 (使用 if-else/switch-case):
表驱动方式:
在这个简单的例子中,表驱动的方式显得更简洁、易于扩展,如果需要添加其他语言的月份名称,只需增加相应的表即可,而不需要修改getMonthNameTableDriven函数的逻辑。
总结
表驱动是一种用数据结构来控制程序逻辑的设计哲学,它能够有效替代复杂的条件语句,提高代码的可读性、可维护性和扩展性。它鼓励将“何事发生”与“如何发生”分离,使得程序更加模块化和灵活。

单元测试用例设计

为方法开发测试用例

如果完全按照测试驱动开发的方式, 则不需要再开发测试用例(因为编写完成的每个方法都已经通过测试).
如果没有使用测试驱动开发, 则编写完成之后就需要开发单元测试用例.
两种线索
  1. 根据方法的规格, 使用基于规格的测试技术开发测试用例.
      • 黑盒测试方法: 等价类划分, 边界值分析
  1. 根据方法代码的逻辑结构, 使用基于代码的测试技术开发测试用例
      • 白盒测试方法: 对关键且复杂的代码使用路径覆盖, 对复杂代码使用分支覆盖, 对简单情况使用语句覆盖.
对于调用了其他类的方法, 需要创建桩程序(或Mock Object)以测试.

为类开发测试用例

每个方法都完成测试之后, 对于复杂类还需要测试类的不同方法之间互相影响的情况. 可以使用基于状态机的技术.
notion image
notion image
Loading...