Fork me on GitHub

架构师 - 5.软件工程

软件工程:一条主线串起

本文按软件生命周期重新梳理,主线只有 6 步:
认识软件工程 → 选过程模型 → 做需求 → 建模与设计 → 测试 → 交付与维护
另有两个特殊专题,各自挂在主线的对应环节下(净室软件工程 → 过程模型,逆向工程 → 交付与维护)。


先建立全局观:全局主线

  认识软件工程 —— 定义 / 三要素 / 生命周期
        |
        v
  选过程模型 —— 瀑布、V、原型、螺旋、增量、CBSE、RAD、RUP、敏捷
        |            专题:净室软件工程 属于这一环节
        v
  做需求 —— 获取 / 分析 / 定义(SRS) / 验证 / 变更管理
        |
        v
  建模与设计 —— UML、4+1 视图、高内聚低耦合
        |
        v
  测试 —— 单元 -> 集成 -> 系统 -> 验收
        |
        v
  交付与维护 —— 系统切换、数据迁移、四类维护
                 专题:逆向工程 / 再工程 属于这一环节

一句话概括:软件工程就是把"需求"工程化地变成"可靠系统",并在交付后持续演进的一整套方法、工具和过程。


一、软件工程概述

要点 内容
定义 用工程化 + 系统化的方法开发、运行和维护软件
目标 高质量、可维护、按期交付、成本可控
三要素 方法(开发技术)、工具(支撑环境)、过程(管理与活动集合)— 缺一不可
生命周期 问题定义 → 可行性研究 → 需求分析 → 总体设计 → 详细设计 → 编码与测试 → 运行维护

三要素是"方法—工具—过程",三者缺一不可。
类比:盖房子要先有设计图、工具和施工流程,软件工程就是避免"边盖边改"的施工规范。


二、软件过程模型:怎么组织开发活动

2.1 一表看懂所有模型

模型 核心思想 适用场景 主要缺点
瀑布 线性顺序,上一阶段输出是下一阶段输入 需求明确且稳定 难以应对变更,用户反馈滞后
V 模型 开发阶段与测试阶段一一对应 需求明确、重质量验证 同瀑布,灵活性差
原型 快速构建原型澄清需求 需求模糊、交互复杂 原型易被误当成成品
演化模型 在原型基础上持续迭代完善 需求逐步明确 过程难管控
螺旋 风险驱动,每轮含计划/风险分析/实施/评估 大型、复杂、高风险 成本高,依赖风险专家
增量 分批交付功能模块 需早期交付核心功能 需良好架构支撑
迭代 反复精化同一范围 技术风险高 范围不扩大
构件组装 / CBSE 复用现有构件快速拼装 有成熟构件库与标准接口 依赖构件质量
RAD 短周期 + 组件 + 用户深度参与 需求明确、需快速上线 不适合大型复杂系统
统一过程 RUP 用例驱动、架构为中心、迭代增量 中大型、需架构治理 过程较重

2.2 三组关键区分

① 迭代 vs 增量

  • 迭代 = 反复打磨同一块(做精)
  • 增量 = 一块块往上加(做多)
  • 实际项目两者结合:既降低技术风险,又逐步扩大功能。

② 瀑布 vs V 模型

  • 瀑布只定义开发阶段;V 模型额外给每个开发阶段配了"验收尺子":
开发(左) 验证(右)
需求分析 验收测试
概要设计 系统测试
详细设计 集成测试
编码 单元测试

③ 原型 vs 演化

  • 抛弃型原型:为了搞清楚模糊的需求,先用最快的方式搭一个能跑的演示版本给用户看,等需求确认清楚了,就把这个原型整个丢掉,再按正规的工程方法从头开发真正可用的系统。之所以要"抛弃",是因为快速拼出来的原型往往代码糙、架构乱、没考虑性能与安全,不适合直接当成品继续做。适合需求极度模糊、用户自己都说不清要什么的场景。
  • 演化型原型:在原型基础上不断加功能、改毛病,原型最终长成交付产品,不会被丢弃。适合需求逐步明确、希望边做边完善的场景。

类比:抛弃型原型像设计师先用硬纸板做个模型确认尺寸和样式,满意后扔掉纸板再用真材实料正式做;演化型原型则是从草模开始,一遍遍打磨,最后那件成品就是它自己。

2.3 螺旋模型的四个象限

  制定计划 ---> 风险分析 ---> 实施工程 ---> 客户评估
      ^                                        |
      +------------- 进入下一轮螺旋 -----------+

每轮都有风险分析 —— 这是螺旋区别于其他迭代模型的独特标志。

2.4 RUP 的四个阶段

初始(定范围与可行性)→ 细化(分析风险、建立基线架构)→ 构建(完成主要功能)→ 移交(交付用户)

三大特征:用例驱动、架构为中心、迭代增量。
类比:拍连续剧 —— 写大纲、搭主场景、集中拍摄、上映。

2.5 模型选型决策树

  按场景选型
    需求明确、稳定       -->  瀑布 / V 模型
    需求不明确、说不清   -->  原型
    大型、复杂、高风险   -->  螺旋
    有构件库、可复用     -->  构件组装 / CBSE
    分批交付、先上核心   -->  增量
    需求变化快、规模适中 -->  敏捷
    用例驱动、架构为中心 -->  RUP

三、敏捷开发

3.1 敏捷开发

更重视 相对次要
个体与交互 过程与工具
可工作的软件 详尽的文档
客户协作 合同谈判
响应变化 遵循计划

需注意:敏捷 ≠ 无文档、无计划,而是"刚好足够"和"及时响应"。

3.2 Scrum 与 XP 的对比

维度 Scrum(重流程) XP 极限编程(重工程实践)
核心 迭代 Sprint、时间盒 结对编程、测试先行
角色 产品负责人、Scrum Master、开发团队 —
工件 产品待办列表、Sprint 待办列表、燃尽图 用户故事
会议 Sprint 计划会、每日站会、评审会、回顾会 —
实践 — TDD、重构、持续集成、小版本发布

区分方法:Sprint / 燃尽图 / 三个角色是 Scrum 的典型特征;结对编程 / TDD / 持续集成是 XP 的典型特征。


四、两个特殊专题:逆向工程与净室软件工程

4.1 逆向工程与再工程

概念 含义
逆向工程 分析已有系统,恢复其设计、结构、需求(重"理解")
再工程 在理解基础上改造系统(重"改造")
正向工程 从需求重新实现
重构 结构优化,不改变外部行为

抽象级别(由低到高):实现级 → 结构级 → 功能级 → 领域级

级别越高,抽象程度越高,信息损失越大。

4.2 净室软件工程

  • 核心理念:预防缺陷,而非事后调试。
  • 三大方法:盒式规约(行为盒 / 状态盒 / 清晰盒)、统计测试、增量开发。
  • 特点:不做单元测试后的调试,靠严格规格说明 + 统计抽样保证质量。

类比:无菌车间 —— 用数学证明和统计抽样预防缺陷,而不是等产品出来后修修补补。


五、需求工程:搞清楚"做什么"

5.1 五个活动构成闭环

  需求获取 -> 需求分析 -> 需求定义(SRS) -> 需求验证 -> 需求管理/变更控制
      ^                                                    |
      +--------------- 变更反馈 / 缺陷修订 ----------------+

5.2 需求的三层分类

  • 业务需求:组织为什么要做(目标)
  • 用户需求:用户要完成什么任务
  • 系统需求:功能需求 + 非功能需求(质量属性:性能、可用性、安全性、可维护性、可移植性、易用性)

质量属性常藏在"系统要跑得稳"这类模糊表述里,需显式提取。

5.3 需求获取技术

访谈、问卷调查、观察、原型、联合应用开发(JAD)、头脑风暴、文档分析。

注意:用户的口头需求 ≠ 最终需求,必须经过分析与确认。

需求验证(确认 SRS 正确、完整且符合用户期望):需求评审、原型验证、测试用例生成、模型检测、用户确认。

通过标志:用户签字确认;发现缺陷需跟踪到文档修订闭环。

5.4 结构化分析三件套

工具 作用
数据流图 DFD 描述"数据从哪来、经过什么加工、到哪去"
数据字典 DD 定义数据流/数据存储的构成
状态转换图 STD 描述状态变迁
判定表 / 判定树 描述复杂条件组合逻辑

DFD 四元素:外部实体、加工、数据存储、数据流。

5.5 SRS 的六大特性

完整性、一致性、可修改性、可追踪性、无二义性、可验证性

类比:签正式合同 —— 把口头约定写成《软件需求规格说明书》,避免日后扯皮。

5.6 需求跟踪与变更

  • 需求跟踪矩阵 RTM:双向跟踪
    • 前向:需求 → 设计 → 代码 → 测试用例
    • 后向:测试用例 / 缺陷 → 需求
  • 变更流程:提出变更 → 影响分析 → CCB 审批 → 实施 → 验证 → 更新基线与文档
  • CCB(变更控制委员会) 负责重大变更审批。

六、建模与系统设计:解决"怎么做"

6.1 UML 13 种图(UML 2.x)

分类 图
结构图(静态) 类图、对象图、构件图、部署图、包图、组合结构图、制品图
行为图(动态) 用例图、活动图、状态图、序列图、通信图、定时图、交互概览图

基本构造块:事物(Things)、关系(Relationships)、图(Diagrams)。

6.2 "4+1"视图(Kruchten)

视图 关注点 面向对象
逻辑视图 系统功能 设计人员
进程视图 并发与同步 集成人员
物理视图 部署与硬件映射 系统工程师
开发视图 模块划分与编译依赖 开发人员
用例视图(+1) 场景,驱动其余四个视图 最终用户

6.3 软件模型的四类

功能模型(DFD)、行为模型(状态机/活动图)、结构模型(类图/E-R 图)、动态模型(序列图/通信图)。

6.4 结构化设计:高内聚、低耦合

内聚七级(由低到高,越高越好)

级别 含义 例子
偶然内聚 模块内各部分毫无逻辑关联,只是碰巧放一起 一个模块里既有"算工资"又有"打日志",二者毫无关系,只因同一程序员顺手合在一起
逻辑内聚 逻辑上相似但功能不同,靠参数决定执行哪个 一个"输入模块"靠 type 参数决定读键盘 / 读文件 / 读网络
时间内聚 因在同一时间段执行而聚合,彼此无功能关联 系统启动初始化:连数据库、载配置、开日志——都只为"启动"这一时刻,步骤间互不相关
过程内聚 按固定流程顺序执行,但各步之间无数据传递 日终处理:先备份 → 再汇总 → 后关账,三个步骤按顺序走但彼此独立
通信内聚 各部分操作同一份数据或同一 I/O 一个模块既"读取订单文件"又"校验订单文件",都针对同一个订单文件
顺序内聚 前一步的输出是后一步的输入,环环相扣 读用户 → 算折扣 → 生成账单,上一步结果直接喂给下一步
功能内聚 所有成分只完成单一、明确的功能(最高级) "计算个人所得税"模块只做这一件事,不夹带任何其他逻辑

目标:设计时追求功能内聚,坚决避免偶然内聚、逻辑内聚。

耦合七级(由低到高,越低越好)

级别 含义 例子
非直接耦合 模块间无直接关联,仅通过上层统一调用(最低,最理想) 模块 A 和模块 B 都只被主程序调用,彼此"互不认识"
数据耦合 只通过简单数据值(基本类型)传递 computeTax(income, rate),只传数值型参数
标记耦合 传递整个数据结构,但只用其中部分字段 传一个 Employee 结构体给打印模块,但只用姓名、工号两个字段
控制耦合 传递控制标志,被调模块据以选择执行分支 process(data, isBatch),用 isBatch 决定走批量还是单条逻辑
外部耦合 多个模块都访问同一全局简单变量 / 外部设备 多个模块直接读写同一个全局配置文件或同一台打印机
公共耦合 多个模块共享同一公共数据区(全局数据结构) 多模块共享同一个全局用户会话对象,一处修改会影响所有使用者
内容耦合 一个模块直接访问 / 修改另一模块内部数据,或跳转进其体内(最高,最差) 模块 A 直接改写模块 B 的局部变量,或用 goto 跳到模块 B 中间

目标:优先数据耦合,尽量压低到标记 / 控制耦合以下,严禁内容耦合。

模块四要素:输入、输出、处理功能、内部数据。

类比:像函数签名,四要素定义清楚,别人才好调用。

6.5 界面设计要点

原则:一致性、反馈、容错、简洁、可访问性;减少用户记忆负担、提供清晰导航、保持操作可逆、及时反馈。

6.6 面向对象设计

类的三分法 BCE:

  • 边界类(Boundary):与外部参与者交互的接口
  • 控制类(Control):封装业务逻辑与控制流
  • 实体类(Entity):封装持久化信息

SOLID 五原则:

原则 含义
S 单一职责 一个类只负责一项职责
O 开闭原则 对扩展开放,对修改关闭
L 里氏替换 子类可替换父类
I 接口隔离 客户端不依赖不需要的接口
D 依赖倒置 依赖抽象,而非具体实现

七、软件测试:验证"做得对不对"

7.1 测试分类维度

维度 类型
是否运行程序 静态测试(评审、走查、静态分析) / 动态测试(运行程序)
是否看内部结构 黑盒(看功能规格) / 白盒(看代码结构)
测试阶段 单元 → 集成 → 系统 → 验收

7.2 白盒覆盖标准(由弱到强)

  语句覆盖 -> 判定覆盖 -> 条件覆盖 -> 判定条件覆盖 -> 条件组合覆盖 -> 路径覆盖
  弱 --------------------------------------------------------------> 强

7.3 黑盒常用方法

等价类划分、边界值分析、错误推测、因果图、判定表、场景法。

最常用:等价类划分 + 边界值分析。

7.4 四个测试阶段

阶段 对象 依据 执行者
单元测试 最小可测试单元(模块/类) 详细设计 开发人员
集成测试 模块间接口 概要设计 开发/测试
系统测试 完整系统(含非功能需求) SRS 测试团队
验收测试 用户确认可交付 用户需求 用户

7.5 集成测试策略

策略 做法 辅助模块
大爆炸 一次性全部集成 无
自顶向下 先主控,逐步下层 桩模块
自底向上 先底层,逐步上层 驱动模块
三明治 上下结合 桩 + 驱动
冒烟测试 每日构建的基本功能验证 —

自顶向下需要桩,自底向上需要驱动。

7.6 性能相关测试区分

类型 关注点
性能测试 响应时间、吞吐量、资源利用率是否达标
负载测试 逐步加压,找系统正常工作的上限
压力测试 在极限/超负荷下的表现与恢复能力
容量测试 系统能承载的最大数据量/用户数

八、交付、转换与维护

8.1 遗留系统处置策略

继续使用 → 包装(封装旧系统、暴露新接口) → 再工程 → 替换 → 退役

决策依据:业务价值、技术债务、维护成本、战略重要性、迁移风险。

8.2 新老系统切换策略

策略 成本 风险
直接切换 低 高
并行运行 高(双系统并行) 低
逐步切换(分阶段) 中 中
试点运行 中 中

选择关键:业务连续性、数据一致性、回退机制。

8.3 数据迁移 ETL

抽取(Extract) → 转换(Transform) → 加载(Load)
迁移前清洗与校验,迁移后验证一致性,必须制定回退方案。

8.4 软件维护四类型

类型 目的 类比
改正性 修复缺陷 修故障灯
适应性 适应环境变化(新系统/新法规) 换雪地胎
完善性 新增功能或改进(占比最高) 加装导航
预防性 防患未然,为将来可维护性做准备 做全面保养

判断方法:看修改目的。完善性维护占比最高,改正性维护最紧迫。
维护后必须做回归测试。


结尾:架构是取舍的艺术

内核其实只有一句话——软件工程的所有决策都是权衡。

  • 选瀑布还是敏捷,是"计划确定性"与"变化响应速度"的取舍;
  • 要高内聚低耦合,是"当下开发效率"与"长期维护成本"的取舍;
  • 遗留系统是包装还是重建,是"迁移风险"与"技术债务"的取舍。

对应到架构实践,就是要识别系统的三类关键点:

  • 敏感点:某个质量属性对某个架构决策特别敏感(如加密强度直接影响安全性);
  • 权衡点:同时影响多个质量属性,且彼此冲突(如缓存提升性能却降低一致性);
  • 风险点:可能造成严重后果的架构决策。

过程模型决定了你什么时候知道真相,设计原则决定了你改真相要付多少代价,而维护策略决定了这套系统还能活多久。

posted @ 2026-10-08 21:26  秋夜雨巷  阅读(7)  评论(0)    收藏  举报