概述
[SWEBOK2004] 将软件构造定义为:通过编码、验证、单元测试、集成测试和调试等工作的结合,生产可工作的、有意义的软件的详细创建过程。
活动
软件构造的主要活动包括:
- 详细设计
- 编程
- 测试
- 调试
- 代码评审
- 集成与构建
- 构造管理
详细设计
- 有些项目会将主要的详细设计工作分配在软件构造阶段完成
- 不论是哪种项目,在软件构造阶段都不可避免的会涉及到详细设计的调整工作。因为编程语言是软件设计的一个重要约束,随着编程工作的进行和深入,人们可能会发现与预想不一致的情况和更多的约束,这个时候就需要在软件构造阶段修改详细设计方案。
- 软件构造阶段详细设计使用的方法与技术与软件设计阶段是一样的,只是应用在更小的规模上。
编程
编程是软件构造的核心活动,其目的是生产高质量的程序代码。程序代码的典型质量包括:
- 易读性:程序代码必须是易读的,看上去“显而易⻅是正确的”。易读性是编程最为重要的目标,它可以使得程序更容易开发,尤其是易于调试;可以使得程序更容易维护,减少理解代码的难度和成本;可以使得程序易于复用。
- 易维护性:除了易读之外,易维护性要求程序代码易于修改(遵守开闭原则)
- 可靠性:程序代码必须是可靠的,要执行正确,并妥善处理故障
- 性能:程序代码必须是高性能的,包括时间性能和空间性能,需要进行详细的数据结构和算法设计
- 安全性:不要遗留程序漏洞,不要出现重要信息的泄漏(例如内存数据区泄漏)
编程的主要技术有
- 构造可理解的源代码的技术,包括命名和空间布局;
- 使用类、枚举类型、变量、命名常量和其它类似实体;
- 使用控制结构;
- 处理错误条件——既包括预计的错误,也包括未预期的异常;
- 预防代码级的安全泄露(例如,缓冲区超限或数组下标溢出);
- 使用资源,用互斥机制访问串行可复用资源(包括线程和数据库锁);
- 源代码组织(组织为语句、例程、类、包或其它结构);
- 代码文档;
- 代码调整。
测试
通常来说,程序员每修改一次程序就会进行最少一次单元测试,在编写程序的过程中前后很
可能要进行多次单元测试,以证实程序达到了要求,没有程序错误。集成测试一般在单元测
试之后,用来测试多个单元之间的接口是否编程正确。
测试驱动方法和持续集成方法
调试
调试主要分为三个部分:重现问题、诊断缺陷和修复缺陷
修复缺陷时需要注意:
- 一次只修复一个缺陷
- 修改前保留旧版本的备份,如果项目使用了配置管理系统,这个工作回由配置管理工具完成,否则就需要由程序员手动完成
- 使用测试和评审验证修复的有效性
- 检查和修复类似的缺陷,这可以在代码搜索、程序切片等工具的帮助下进行
代码评审
代码评审是对代码的系统检查,通常是通过同行专家评审来完成的。通过评审会议可以发现并修正之前忽略的代码错误,从而同时提高软件的质量和开发者的技巧。
代码评审一般分为正式评审、轻量级评审和结对编程
[Cohen2011]的“实践经验”
- 就算不能评审全部的代码,最少也要评审一部分(20–33%)代码,以促使程序员编写更好的代码。
- 一次评审少于 200–400 行的代码。
- 目标为每小时低于 300–500 LOC 的检查速率。
- 花足够的时间进行正确缓慢的评审,但是不要超过 60–90 分钟/每次。
- 确定代码开发者在评审开始之前就已经注释了源代码。
- 使用检查列表,因为它可以极大地改进代码开发者和评审者的工作。
- 确认发现的缺陷确实得到修复了。
- 培养良好的代码评审文化氛围,在这样的氛围中搜索缺陷被看做是积极的活动。
- 采用轻量级,能用工具支持的代码评审。
集成与构建
- 在以分散的方式完成程序基本单位(例程、类)之后,软件构造还需要将这些分散单位集成和构建为构件、子系统和完整系统。
- 集成有大爆炸式集成和增量式集成两种方式。实践中增量式集成有着更好的效果。(自顶向下、自底向上等多种方式)
- 构建将可读的源代码转换为标准的能在计算机上运行的可执行文件。构建过程需要配置管理工具的帮助。
构造管理
构造管理主要包括构造计划、度量和配置管理三个任务。
构造计划
构造计划根据整个项目的开发过程安排,定义要开发的构件与次序,选择构造方法,明确构造任务并分配给程序员。构造方法的选择是构造计划活动的关键方面,影响着源代码的质量
度量
软件构造阶段的产品度量主要围绕源代码展开,常见度量包括:
- 每个类或者方法的复杂度
- 每个类或者方法的代码行数
- 每个类或者方法的注释行数
配置管理
实践方法
重构
为什么要重构
- 因为无法预计到后续数年的修改,导致软件开发阶段的设计方案不能满足修改要求;
- 随着修改次数的增多,软件设计结构的质量越来越脆弱,很难继续维持可修改性。
重构的定义
[Fowler1999]:修改软件系统的严谨方法,它在不改变代码外部表现的情况下改变其内部结构。
- 不改变代码的外部结构是指不改变软件系统的功能。
- 改变代码的内部结构是指详细设计结构的质量,使其能够继续演化下去
重构的时机
- 增加新的功能时。需要注意的是重构发生在新功能增加完成之后,用来消除新功能所添加代码导致的坏味道;而不是发生在新功能添加之前,重构不改变代码外部行为,不是能够实现新功能添加的方法。
- 发现了缺陷进行修复时。诊断缺陷时如果发现代码存在坏味道或者修复代码会引入坏味道(code smell),就需要进行重构。
- 进行代码评审时。如果在评审代码时发现了坏味道,就需要进行重构。
Code Smell 坏味道,是设计结构低质量的表现,例如:
- 太⻓的方法,往往意味着方法完成了太多的任务,不是功能内聚的,需要被分解为多个方法。[McConnell2004]认为如果方法代码⻓度超过了一个屏幕,就需要留心注意了。
- 太大的类,往往意味着类不是单一职责的,需要被分解为多个类。
- 太多的方法参数,往往意味着方法的任务太多或者参数的数据类型抽象层次太低,不符合接口最小化的低耦合原则,需要将其分解为多个参数少的方法或者将参数包装成对象、结构体等抽象层次更高的数据类型。
- 多处相似的复杂控制结构,例如多处相同类型的Case结构,往往意味着多态策略不足,需要使用继承树多态机制消除复杂控制结构。
- 重复的代码,往往意味着隐式耦合,需要将重复代码提取为独立方法。 一个类过多使用其他类的属性,往往意味着属性分配不正确或者协作设计不正确,需要在类间转移属性或者使用方法委托代替属性访问。
- 过多的注释,往往意味着代码的逻辑结构不清晰或者可读性不好,需要进行逻辑结构重组或者代码重组。
测试驱动
- 测试驱动开发又被称为测试优先(Test First)的开发,随着极限编程方法的普遍应用而得到普及。
- 测试驱动开发要求程序员在编写一段代码之前,优先完成该段代码的测试代码。测试代码通常由
测试工具自动装载执行,也可以由程序员手工执行。完成测试代码之后,程序员再编写程序代码,并在编程中重复执行测试代码,以验证程序代码的正确性。
结对编程
两个程序员挨着坐在一起,共同协作进行软件构造活动
Reference
- 南京大学软件学院2022春季学期《软件工程与计算二》