软件工程笔记
软件工程
前言
- 写编译器的时候深感自己的软工能力太弱了,速刷了一下很久就想自学的软工
鸭大18计科没有软工课实在是太离谱了- UML 部分比较简略
绪论
- 软件:程序+文档
- 软件测试和系统维护开发占比高
- 软件分类
- 系统软件:e.g. 操作系统,编译器
- 支撑软件:e.g. 中间件
- 应用软件
- 开发的本质:不断地抽象
- 问题->需求->设计->实现
- 软件工程是一门交叉学科,涉及计算机,工程,管理学等
- 好的软件是设计出来的
软件过程
- 软件生存周期
- 软件生存周期模型/软件开发模型:软件过程、活动、任务的结构组织框架
- 常见软件构件: WEB 应用、包或框架、独立环境系统等
软件生存周期过程
- 基本过程:获取,供应,开发,运行维护
- 支持过程:e.g. 文档,配置,审计,验证
- 组织过程:e.g. 基础设施,人力资源
- 个人理解,广义上的软件工程就是涉及开公司的方方面面
瀑布模型
- 分阶段开发:系统需求、软件需求、需求分析、设计、编码、测试、运行
- 不允许跨阶段返工
- 优点:强调编码前的设计
- 缺点:必须提前确定需求;过分强调文档;直到开发完全结束前都不能演示系统
- 客户其实很难在一开始就知道自已要什么,更不必提清晰地表达需求,让开发人员捕获需求
- 通常用于较成熟的领域开发
增量模型
- 需求可以分多阶段,每个增量的开发是一个瀑布模型
- 优点:第一个版本的成本低
- 缺点:若需求变化大,现有代码的修改成本高
- 构造:不需要交付的增量;发布版本:需要交付的增量
演化模型
- 根据用户的反馈不断获取新需求,每次演化的开发是一个瀑布模型
- 优点:需求不明确时方便快速迭代
- 缺点:管理需求高,容易不写文档;没有风险控制
喷泉模型
- 用于描述面向对象的开发过程,软件开发各阶段间没有明显边界
螺旋模型
- 反复:规划、风险分析、开发、评估
- 类似增量模型,但是决策的风险源不止有需求;类似演化模型,但是加入了风险分析
感觉模型种类字眼没有必要扣那么死,弄得很绕
快速原型模型
- 开发原型是为了帮助获取用户真正的需求;测试项目中某些部分的技术、成本、进度等的可行性;即做一个 demo 看看
面向复用的软件开发模型
- 面向可复用的软件构件的集成框架开发过程
软件需求
产品经理入门- 需求
- 分类:功能,性能,外部接口,设计约束,质量属性等
- 基本性质:必要的,无歧义的,可测试的,可跟踪的,可测量的
需求捕获方式:自悟,交谈,观察,会议,提炼等
- 需求规约 SRS :需求陈述的正式文档,概念模型
- 性质:重要性和稳定性程度,可修改的,完整的,一致的
- 事实上的交付合同
软件开发方法学
结构化方法
- 结构化设计哲学:一切都是数据流动
结构化分析
- 建立数据流图 DFD ;自顶向下,逐层分解数据加工;建立数据字典
- 数据源:数据流的起点,系统外的实体
- 数据谭:数据流的终点,系统外的实体
太抽象了,工作中感觉根本不会这么一丝不苟- 复杂度控制:上层数据可以打包,图元个数经验上应控制在\(7\pm2\)
结构化设计
总体设计
总体设计分三阶段
首先,把数据流图初步转化为模块结构图 MSD;自顶向下,逐层细化;确定系统与外部的接口
- 分类处理:变换型数据流图、事务型数据流图
- 大体上是一个机械过程
其次,MSD 进一步模块和模块化;设计全局数据结构和每个模块的接口
- 准则:高内聚,低耦合
- 体现设计人员的智慧
- 模块的作用域要在控制域之内
最后,设计复审
耦合
内容耦合:一个模块直接修改/操作另一个模块的数据
公共耦合:两个以上的模块共同引用一个全局数据项
控制耦合:一个模块向另一个模块传递一个控制信号
标记耦合:模块间通过界面传递复杂类型的数据
数据耦合:模块间通过参数传递基本类型的数据
原则:使用数据/标记耦合,少用控制耦合,限制公共耦合的范围,避免内容耦合
数据设计
- 文件设计
- 大的非结构化数据: e.g. 多媒体信息
- 大的松散数据: e.g. 历史记录、档案
- 非关系层次化数据: e.g. 系统配置文件
- 临时数据
- 数据库设计
- 详见数据库
详细设计
- 结构化程序设计:代码块仅仅通过顺序选择循环进行连接,且仅有单一入口和单一出口
- “ goto 有害 ”:但偶尔有时候需要在效率和结构上平衡
- 常见程序表达
- 伪代码
- 流程图/程序框图
- PAD 图:支持逐步求精设计
- N-S 图:支持逐步求精设计
- 判定表/判定树
软件设计说明书&代码评审
- ...
敏捷开发
概念
- 敏捷开发是应对快速变化的需求的一种增量式软件开发,是一种哲学理念和一系列开发指南的综合
- 一般适应小而高度自主的团队开发小软件
- 个人认为敏捷开发的核心是以人为本,重视每个人都发挥主观能动性,重视团队的力量而不是工具的作用
本人第一次实习完全就是敏捷开发,体会很深
敏捷原则
- 敏捷联盟宣言
- 个体和交互胜过过程和工具:团队沟通强过个人编程能力;不要过分夸大工具的作用,要考虑团队的学习成本;团队的构建比环境的构建重要得多
- 可以工作的软件胜过面面俱到的文档:没有文档是灾难,过多的文档更是灾难,尽可能维护一份系统原理和结构方面的文档
- 客户合作胜过合同谈判:频繁与客户沟通反馈强过合同陈述,换句话说客户不单纯是甲方,而应与乙方协同工作;这有利于共同把事情做好,但是另一方面,可能导致无穷无尽的需求
- 响应变化胜过遵循计划:计划不能考虑过远,响应变化的能力是决定软件成败的关键
- 敏捷原则
- 尽早地,持续地交付
先跑起来 - 开发后期也欢迎改变需求
- 频繁交付,用户可见
- 客户和开发人员合作开发
- 激励信任开发人员,提供环境和支持
- 强调面对面交流
- 首要的进度标准是软件本身
- 提倡可持续的开发速度
- 不断关注优秀的技能和设计
- 尽可能简单
- 整个团队对整个项目负责任,成员也需要了解他人的工作
- 团队不断反省调整
- 尽早地,持续地交付
极限编程
- 极限编程 XP:敏捷方法中最显著的一个,由一系列简单相互依赖的实践组成
- 客户作为团队成员
- “用户素材”,概念类似工单
- 短的交付周期
- 验收测试
- 结对编程:尽可能两个人配对写
- 测试驱动的开发,即尽可能先把测试流程跑起来
- 集体所有权:没有程序员对一个特定模块或技术单独负责,所有人都应该关心负责一切
扁平化管理? - 持续集成
- 可持续的开发速度
- 开放的工作空间
- 规划游戏:开发人员提供多种开发选择,业务人员根据成本选择用户素材
- 简单的设计
- 尽可能用最简单的实现方法
- 只有在较复杂的基础设施(数据库,远程过程调用,多线程等)使用明显合算时才引入
- 不允许出现重复代码
- 重构
- 隐喻:架构善用比喻
- 极限编程过程:策划、设计、编码、测试
敏捷设计
- 敏捷设计
- 避免僵化性:连锁改太多导致难以改动
- 避免脆弱性:改动会影响概念上无关的部分
- 避免粘固性:通用功能模块和某部分耦合程度太高难以复用
- 避免粘滞性
- 软件粘滞性:改动很容易破幻整体设计
- 环境粘滞性:环境迟钝低效,如编译时间过长
- 避免不必要的复杂性:设计中不要没有用的成分
- 避免不必要的复制:不要简单 CTRL-CV
- 避免晦涩性:模块不要难以理解
- 基本途径
- 不进行预先详细设计,预设计尽可能简单
- 多单元测试和验收测试
- 持续迭代
- 坚持多种敏捷设计原则
- 使用设计模式
- 设计模式:经验的总结,对软件开发中一些反复出现、具有共性的问题给出的良好解决方案
面向对象方法
- 面向对象设计哲学:一切都是对象
UML
- UML 是系统分析和设计的工具,是面向对象软件工程使用的统一建模语言,是一种图形化的语言,主要以图形方式表示,是一种开放的标准
- 多层次的设计图:每种图都展示系统的一个侧面
- 维基
- 系统静态部分
- 类图
- 构件图:说明构件之间的依赖关系
- 组合结构图:可以看做类图的部分
- 对象图:和类图类似,实例的快照
- 部署图:系统物理视图
- 制品图:展示制品之间的依赖关系
- 系统动态部分
- 用例图:需求模型
回想起自己实习的时候问 mentor 什么是用例现在有点难绷 - 状态图
- 活动图:说明对象的操作流程,类似流程图
- 时序图(顺序图)
- 通信图:说明对象间的交互情况
- 用例图:需求模型
- UML 术语
- 类:一组具有相同属性、操作、关系和语义的对象的描述
- 对象:类的实例
- 类间关系:依赖、关联、聚合、组合、泛化(继承)、实现
太抽象了,很多概念术语这里暂略
- 类图
- 用例图
- 时序图
面向对象分析
- OOA 过程
- 定义用例
- 发现对象
- 定义属性与操作
- 建立对象间的关系
- 划分包
- 建立顺序图、状态图等
- 识别关系策略
- 个人认为,总体上需要领域知识和常识
- 继承
- 领域分类学知识
- 常识
- 根据继承定义
- 考虑属性与操作的适用范围
- 考虑领域复用
- 关联
- 考虑对象间的静态联系/责任
- 考虑关联的属性与操作
- 分析关联多重性
面向对象设计
- 根本目标:提高软件生产率、提高质量
- 和结构化设计不同,OOD 和 OOA 之间不存在鸿沟,不是转化关系,是调整和增补
- 实现考量
- 编程语言
- 硬件
- 复用支持
- 数据管理系统
- 可能的细节
- 能复用尽可能复用
- 增加一般类
- 根据编程语言调整细节
- 提高性能
- 合并通讯频繁的类
- 永久存储
- 细化对象分类
- 人机交互
- 数据管理
- 文件
- 基本策略:一个类一个文件,一个对象一个记录
- 继承处理:父子分文件存储,继承信息另外存储
- 实现“对象存储器”类,并且增加一个接口来供类实现
- 关系数据库
- 通常第三范式
- 文件
软件测试
静态分析:对源代码进行分析审查
动态分析:功能测试/黑盒测试;结构测试/白盒测试
黑盒测试
- 把程序当做黑盒,尽可能穷举输入;数据驱动
- 事务流图:事务的抽象,一种数据流图,太复杂,不怎么用
- 等价类划分
- “找代表”
- 等价类:输入域的一个子集,该集合中的每个输入数据对于揭示程序中的错误都是等效的
- 有效等价类
- 输入有效的等价类
- 寻找测例覆盖尽可能多的有效等价类,即覆盖多一些测试功能点,直至覆盖所有有效等价类
- 无效等价类
- 输入无效的等价类
- 寻找一个测例覆盖一个无效等价类,直至覆盖所有无效等价类
- 边界值分析
- 找刚好在测试边界的测例以及刚好超过测试边界的测例
- 用得多
- 因果图
白盒测试
- 根据程序逻辑结构测试
- 路径测试 PX
- 执行程序所有可能执行的路径
- 达不到
- 语句测试 P1
- 至少执行程序中所有语句一次
- 语句覆盖是最弱的逻辑覆盖准则
- 分支测试 P2
- 至少执行程序中所有分支一次
- 条件组合测试
- 设计足够的测试用例使所有条件取值组合至少执行一次
- 一定符合分支测试
- 循环
- 选取关键路径测例
软件测试步骤
- 单元测试
- 白盒测试
- 同时侧重代码审查等静态分析手段
- 回归测试:修改旧代码后,重新进行测试以确认没有引入新的错误
- 集成测试
- 黑盒测试
- 注重接口以及各模块的衔接问题
- 冒烟测试:即入门测试,初步地测试,侧重看软件是否能全流程跑起来,系统是否值得测试
- 有效性测试
- 黑盒测试
- 即确认测试,验收测试
- 用户参与,用户为主
- Alpha测试:产品发布前开发单位内部综合测试
- Beta测试:产品正式投入市场前有关用户试用测试
- 系统测试
- 实际环境中检测整个系统是否协调
- 功能测试:运行软件系统所有功能,验证有无错误
- 恢复测试:使软件崩溃,检验软件是否可恢复,开销几何
- 安全性测试:试图入侵系统
- 强度测试:在非正常强度(数量/频率/容量等)下运行系统
- 可用性测试:测试使用合理性
- 部署测试:即配置测试,测试多平台环境运行情况
测试驱动开发
- 敏捷开发中常用
- 在开发功能前,先编写测例,再进行开发直至满足测例,循环往复直至满足所有需求
- 测试驱动原则
- 测试隔离:不同代码测试应相互隔离
- 测试列表:任何阶段增加需求时先将相关功能点测例加入测试列表,再继续手头的开发
- 先写断言:先用断言,再写相应功能代码
- 可测试性:代码的设计和编写应具有较强的可测试性,e.g. 别轻易卡在某处影响其它部分的测试
- 及时重构
面向对象测试
- 单元测试:类测试
- 集成测试
- 基于线程
- 基于使用:先测很少使用服务的类(独立类),逐步测试来增加闭包
Web测试
- 众多测试框架
软件项目管理
项目经理入门- 通过案例和书的经验学习
- 四大核心知识领域:范围、时间、成本和质量
- 四大辅助知识领域:人力资源管理、风险管理、沟通管理和采购管理
- “人是项目成败与否最重要的因素”
- “传统软件工程是制造业,互联网是服务业”
软件开发工具
- 各种 IDE 等
参考
软件工程笔记
http://example.com/2022/09/06/软件工程笔记/