软件工程知识点归纳
软件工程概述
软件危机
定义:软件危机是指在计算机软件的开发和维护过程中所遇到的一系列严重问题。
原因
- 技术原因:软件规模大,软件复杂度高
- 管理原因:软件开发缺乏正确的理论指导
如何克服软件危机:软件工程
软件开发链条的放大作用:
- 只有早期发现问题,才会尽量减少损失
- 客观规律:用户的牙膏不会一下子挤完(用户的需求是不断变化的)
消除软件危机的途径
对计算机软件正确认知
- 技术和方法
- 软件工具
- 管理措施
软件工程
- 方法
- 工具
- 过程
软件
- 文档
- 程序
- 数据
软件工程是从技术和管理两方面研究如何更好地开发和维护计算机软件的一门新兴学科
软件工程的主要目标:高效开发高质量软件,降低开发成本
软件工程基本原理
- 用分阶段的生命周期计划严格管理
- 坚持进行阶段评审
- 实行严格的产品控制
- 采用现代程序设计技术
- 结果应能清楚地审查
- 开发小组的人员应该少而精
- 承认不断改进软件工程实践的必要性
系统工程
目的:使得人们能够确保在正确的时间使用了正确的方法在做正确的事情
统一建模语言(UML)
提供了一整套对系统建模的基础设施
是一种工具而不是一种方法
重要功能:可视化,规格说明,构造,文档化
UML的构成和视图:
- 逻辑视图
- 进程视图
- 开发视图
- 物理视图
- 用例视图*
基于UML的系统开发过程
- 需求分析
- 分析和设计
- 软件架构建模
- 实现
软件工程开发方法
三个要素
- 方法 完成软件开发各项人物的技术,回答“如何做”
- 工具 为方法的运用提供自动或半自动软件支撑环境,回答“用什么做”
- 过程 为获得高质量的软件要完成的一系列任务的框架,规定完成各项任务的步骤,回答“如何控制,协调,保证质量”
分类:传统的和面向对象的
面向对象
特点
- 尽可能模拟人类习惯的思维方式
- 是一个主动地多次反复迭代的过程
- 概念和表示方法上的一致性,阶段间平滑过渡
- 由特殊到一般的归纳思维过程,由一般到特殊的演绎思维过程
- 降低复杂性,提高可理解性
- 促进了软件重用
软件开发过程
软件生命周期与开发过程
软件生命周期:更一般化
软件开发过程:更具体化
上名周期是软件开发宏观上的框架
软件生命周期的划分
- 软件定义
- 软件开发
- 运行维护
可行性分析与开发计划
分析是否值得开发,给决策者提供做与不做的依据(高层次的需求分析和设计)
方面:
- 技术可行性
- 经济可行性
- 社会可行性
输出:描述可行性,拟定开发计划
需求分析
确定软件开发可行的情况下,对目标软件未来需要完成的功能进行详细的分析
由于用户的需求随着项目的进展和理解处在不断的变化之中,应对需求进行变更管理
需求:
- 功能性需求
- 非功能性需求
输出:需求规格说明书
软件设计
软件设计是在需求分析的基础上寻求系统求解的框架,如系统的架构设计、数据设计等。
分类:
-
概要设计
输出:概要设计说明书
-
详细设计
输出:详细设计说明书
尽可能保证系统设计结构在整体上的稳定性
程序编码
要忠于设计
软件测试
程序编码后需要对代码进行严密的测试,以发现 软件在整个设计过程中存在的问题并加以纠正。
阶段:
- 单元测试
- 集成测试
- 系统测试(包括验收测试)
测试方法:
- 黑盒方法
- 白盒方法
软件维护
特点:时间最长+成本最高
分类
- 改正性维护(成本最高)
- 适应性维护
- 完善性维护
- 预防性维护
传统的软件生命周期模型
最基本和有效的可供选择的软件开发模型。
瀑布模型
现在仍然是应用得最广泛的过程模型。
特点:
- 阶段间具有顺序性和依赖性,文档驱动
- 推迟实现,不急于编写代码
- 质量保证的观点
每个阶段都必须完成规定的文档,没有交出合格的文档就是没有完成该阶段的任务。
实际的瀑布模型
可以在一定程度上解决变化的问题
计划驱动 在对系统整体的把控和协调上,具有优势,因此适合规模较大的系统或分布式开发模式。
快速原型模型
快速原型模型(Rapid Prototype)的主要作用是在用户和 开发者之间起到“桥梁”的作用。
目的:降低风险
特点
- 对系统进行简单和快速的分析,快速构造一个软件原型。
- 反复评价和改进模型,获取用户真正的需求
- 可以尝试未来的新技术
问题
- 原型和产品开发技术和手段不一样
- 原型一般会被抛弃
增量模型
软件产品作为一系列增量构件来设计、编码、集成和测试。
- 瀑布模型:力求一次性给用户完整的系统。
- 增量模型:逐步增加系统功能。
- 需要开放的架构设计。
区分:快速原型在生命周期头部添加原型,增量模型在尾部添加功能增量
螺旋模型
每个阶段前都增加了风险分析的快速原型模型
基本思想:使用原型及其它方法 尽量降低⻛险。
喷泉模型
🌟特性:迭代+无缝
- 迭代:逐步求精,由小及大
- 无缝:设计与编码之间对应更好
为避免喷泉模型的过分无序,把一个线性过程作为总目标。
敏捷软件开发
开发团队和用户反馈推动产品开发(强调与客户的合作)
-
增量的开发方式
分批分期地交付用户产品,先实现必要性的用户用例
用例:一件用户通过系统完成的有价值的目标,不是一个具体的功能
-
迭代的开发方式
不强调新功能的加入,是追求细化的过程
不指望构建的软件是客户想要的,先构建后修改(逐步求精),多次反复找到客户需要的软件
极限编程(XP)
主要目的:降低需求变化的成本
SCRUM过程
XP注重实践,SCRUM注重过程。
- 需求——产品需求积压
- 开发过程——多个冲刺
角色
- 产品拥有者:确定产品积压的优先级,开发团队和客户的联络点
- 利益相关者:客户代表
- 专家:开发团队和产品拥有者的联络点
DevOps过程
强调开发和运维之间必须紧密合作
是一组 过程,方法与系统 的统称
核心目标:自动化和可持续交互
-
持续集成
提交新代码后立即构建单元测试以确定能否集成
- 纵向集成:应用全生命周期
- 横向集成:打通了架构,开发,管理,运维等部门
-
持续交付
持续集成的延伸
集成后的代码→类生产环境→生产环境
需求分析
目标:系统是什么,以及存在哪些约束条件。
挑战:
- 分析人员需要熟悉,了解和掌握相关的业务领域
- 每个人对于系统的全局都是一个局部
- 如何采取措施应对客户想法的变化
可行性分析
给出系统一个合理可行的方案。
评估出系统初步的开发费用。
需求列表:
- 由客户主导制定的需求文档称为用户(业务)需求
阅读对象是委托方和客户。
- 由开发者主导制定的需求文档称为系统需求
系统需求是对用户需求的细化和完善 是用户需求的开始
涉众(Stakeholder)
是与目标系统相关的一切人和物
- 最终用户(直接涉众):系统的直接使用者
- 投资者
- 业务提出者
- 业务管理者
- 业务执行者
访谈
目标产物:规格说明 分类:
- 正式: 系统分析员提出事先准备好的问题
- 非正式:开放性场景
用例
识别角色——>帮助寻找用例
角色是存在于系统 边界之外 的 角色可以是人,也可以是其他软件系统 eg.“定时器(Timer)“
寻找用例
- 关键业务实体:关键概念或技术术语
- 基本业务实体的加工和组合
- 系统业务的利用和计算
- 与其他系统的交互
分类
- 业务用例:从客户角度出发
- 系统用例:从计算机系统角度出发
用例规约
- 处理流程: 基本事件流:正常执行(include) 备选事件流:异常或特殊情况(extend)
- 关键性:该功能在系统中的关键程度(功能性需求)
- 关联用例:其他相关用例(非功能性需求)
🌟包含关系
依赖:虚线+简单箭头
通用过程定义其主用例的 基础性的功能
构造型 << include >> 的关系与依赖它的主用例相连(指向基础用例)
💫主用例和包含用例应是平行关系,而非逻辑分解(粒度水平及抽象级别相等)
🌟拓展关系
在某些 特殊情况 下的处理
箭头方向指向主用例
- 般来说被包含用例属于无条件发生的用 例,而扩展用例属于有条件发生的用例;
- 被包含用例提供的是间接服务,扩展用例提供的是直接服务;
- 而且扩展用例在用例规约中一般作为基本事件的备选流而存在。
即使是在非常复杂的系统中,每个用例图中的用例数目一 般要避免超过 15 个。
活动图
控制流:直线+简单箭头
开始,结束:实点●,实心点◉
action:圆角矩形
branch:菱形♢ + 条件[]
fork:分支
merge:分支合并+分支的关系{}
对象:矩形
activity:action的集合
泳道技术(swinlane):将活动组织成用线分开的不同区域来表示
功能性需求
-
系统功能需求
是系统功能的主要组成部分,也是另外两类需求功能的发起者
-
交互需求
提供数据的输入
-
外部接口需求
数据流图(DFD)
🌟画图重点
要点
- 信息流不能是动词
- 实体到实体的信息流动
- 处理要有输入输出
- 处理名不能是名词
分层细化时要注意层与层之间的连续性与一致性
非功能性需求
需求验证
验证需求一致性
- 自然语言书写(大部分)
- 形式化描述:使用软件工具
需求跟踪
给出针对每个源需求及对应目标实现,将它们通过某种方式联系起来。
实践中常采用 需求跟踪矩阵(RTM) 对变更进行管理。
软件架构的构建
软件架构也被称为 软件体系结构
软件架构的”4+1”视图
UML的构成和视图:
- 逻辑视图
- 进程视图
- 开发视图
- 物理视图
- 用例视图*
架构的模型:结构模型、框架模型、动态模型、过程模型和功能模型
软件架构的基本元素
构建:软件模板单元
连接件:构建之间的交互
配置:构建与连接件之间的拓扑约束
常见体系结构风格
数据流⻛格:管道与过滤器
调用/返回⻛格:层次结构、正交软件结构、客 户机/服务器结构、浏览器/服务器结构
独立构件⻛格:MVC结构
数据中心⻛格:仓库/黑板系统
管道与过滤器
特点:构件读取输入的数据流,产生输出的数据流
构件被称为过滤器
可以使软件具有良好的信息隐藏性和模块独立性,从而产生高内聚、低耦合的特点
层次系统
特点:每一层为上一层服务,并作为其下层客户
优点:允许将一个复杂的问题分解成一个增量步骤序列的实现
仓库系统
特点:中央数据结构+若干个独立构件
由控制原则分出的子类:
- 传统型数据库:仓库输入流中的事件触发进程
- 黑板系统:由中央数据结构的状态触发进程
优点:松耦合代理数据存取
正交软件架构
构成:层和线索
- 层是一组具有相同抽象级别的构件构成
- 线索是子系统的特例
特点:不同线索之间没有相互调用
优点:每个需求变动仅影响某一条线索
客户机/服务机架构 C/S
服务器:访问与并发性控制、安全性、备份与恢复和全局数据完整性规则。
客户机:提供界面、提交信息、客户端应用逻辑(胖客户)
优点:
- 对于硬件和软件的变化具有极大的适应性和灵活性
- 易于对系统进行扩充和缩小
- 系统中的功能构件充分隔离
浏览器/服务器架构 B/S
是 C/S 的拓展瘦客户
可以减轻安装、配置、升级工作
优点:层与层之间相互独立,可选择任何厂家的产品(平台透明性)
缺点:应用服务器更新成本高 → 微服务技术
MVC架构
模型——视图——控制器
优点:在改进和个性化定制界面及用户交互时不需要重新编写用户逻辑。
软件结构设计
系统将逻辑关系密切的单元划分到一起,形成系统的逻辑划分(基于类模型进行)
将软件使用”包(package)“进行构造。
包及其结构
包与包之间相互嵌套构成包的层次关系:直接嵌套或者通过一个带圈的十字连接符嵌套。
包之间的虚线箭头表示 依赖关系
包图中的一个重要的要求是在包间 不能出现循环依赖
包结构设计
尽量减少包中全局类的数量
要将类更加合理地在包之间划分和组织,以提高各部分的独立性,从而达到更高的内聚性
调整包的宏观结构
类的分析与设计
对未来系统的功能进行总体上的概括并使用 UML 的类图进行表达
基本类的确定
类通常分为 实体类、控制类和边界类
实体类 → 名词
控制类(管理类) → 动宾
边界类 → 外部用户与系统之间的交互对象
类的关系
关联关系( Assosiation ):最常见的关系,表达对象之间的一种“持有”联系
一个类可以具有只想自己本身的关联关系,称为 自反关联
类与对象
对象在UML中通常使用 “对象名:类名” + 下划线来表示
类的细化
管理类和控制类
管理类:同类对象的协调和管理 eg. BoolList 类
控制类:对一个或几个用力所特有的行为进行建模 eg. controller
建模建议:
-
每次只考虑一个任务
-
类与类之间尽可能保持较少的联系
设计优化
有关章节详细论述
补充和确认
使用 UML 中的顺序图对需求场景中涉及的不同对象之间的交互过程进行建模
顺序图
横轴:场景中所涉及的对象横向罗列
纵轴:时间的延续
每个对象的生命线:每个对象垂直向下延伸的虚线
同步消息:实心三角箭头表示 →
异步消息:简单箭头
对象的创建和消息的返回:虚线箭头
顺序图中的逻辑结构
-
opt:可选的内容
-
alt:对分支条件的选择
-
loop(start,end,condition):对循环结构的定义
通信图:对对象之间动态的交互进行可视化建模
-
对象之间通过实线连接
-
连接的一侧描述消息的交互信息
-
交互顺序通过消息前的数字进行标识
场景模拟
使用顺序图对活动图进行确认的方法
界面类(边界类的设计)
基本要求:通过界面使得模型中含有的业务类的某些部分对外部可见
🌟 控制类用来驱动用例中的业务,边界类提供业务服务
eg. Project 实体类可以设置一个界面类 ProjectMask ,进一步可以使用 GUIControl 类,响应用户的功能选择。
代码生成
考虑设计方案向实际运行方式的转变过程,即由概要设计产生出对应的程序代码框架的过程
逆向工程与 CASE 工具
软件开发环境:支持软件开发的工具及其继承机制,也即计算机辅助软件工程(CASE)
两个极端
-
需求分析、概要设计和详细设计过程只进行一次或者迭代增量式地进行。当编码开始后, 对已经完成产品的修改只在程序代码中体现。需求分析、概要设计及详细设计文档不做更新。
-
对每个改动的意愿,都要经过完整的分析、概要设计和详细设计流程,所有的改动都 需要在所属的文档及代码中对应修改,并保证它们的一致性。
逆向工程
将代码的修改反向映射回类图的设计中
单个类的代码实现
类中需要包含的信息如下。
-
每个实例变量,需要指定其类型。
-
每个方法中的参数和返回值,需要指定其类型。
-
每个关联关系,其关联类型、导航方向必须说明。
带有下划线的方法和属性表示静态方法和静态变量(破坏了封装性,要仔细斟酌)。
关联关系的实现
关联关系的实现最终表示为对应类中增加的实例变量
变量存在的具体形式依赖于关联的具体类型
关联
只要类与类存在连接关系就可以用普通关联来表示(使用与被使用的关系)
聚集
-
共享聚集(聚合)
整体方对象消失部分方对象依然存在
-
组合聚集(组合)
整体方对象消失部分方对象也消失
泛化
-
普通泛化(与继承基本相同)
-
受限泛化
附加约束条件,预定义的约束包括:多重,不想叫,完全和不完全
依赖和细化
-
依赖
-
一个模型是独立的,另一个模型不是独立的,
-
强调对象间访问的瞬时性,比如将某些类向其它类进行传递
-
-
细化
对同一个事物在不同抽象层次上描述
其它物理实现(再谈“4+1”视图)
逻辑视图
主要包括系统的功能性需求及类模型
开发视图
包括对子系统和接口的描述
进程视图
包括处理流程,并行性及同步策略等描述
物理视图
对目标硬件及网络等的描述
类的详细设计
详细设计的主要活动
对每个模块应给出详细设计 的方案说明,并最终形成详细设计说明书
类方法的详细设计
结构化程序
- 每种类型的程序块对应不同的逻辑结构,分别是顺序结构、选择结构和循环结构
- 结构化程序设计还要求每个逻辑程序块只有一个入口和一个出口
图形工具
-
程序流程图
-
盒图(N-S 图)
-
PAD 图
表格工具
判定表
语言工具
程序设计语言(Programming Design Language, PDL):又称结构化语言或伪代码
类的行为设计
两种类
- 无记忆对象(Memoryless)或无状态对象(Stateless):主要功能是对业务信息进行管理
- 有记忆的对象或者有状态的对象:行为大多依赖于对象当前的状态
对象的状态及状态变化可以借助状态图(State Diagram)或有穷状态机(Finite State Machine)进行描述
状态图
一般来说,状态图中的状态变化都是针对确定性行为的描述。也就是说,在状态一定、事件相同的情况下,该对象的下一个状态也是相同的。
状态的描述:状态名 +
- entry
- do
- exit
转换:事件[ 条件 ]/动作
- 事件:它是转换说明的主要内容,因为状态图主要是对被动系统的描述,即对外界的 激励事件进行相应的响应。
- 条件:状态间的转换只有在事件被触发并且满足某个特定条件时才会进行
- 动作:表示当转换发生时执行的一个动作,该动作执行的时机是在转换对应的目标状 态的 entry 事件被执行之前,即还未进入目标状态前。
对象约束语言
UML 使用一种标准的对象约束语言(Object Constraint Language, OCL)来对类和对象所依附的条件进行正式定义
eg. 要求学号长度至少为五位数字,并且构成的学号要大于或等于 10000,若使用 OCL 进行约束,则可以写成如下形式:
context Student inv regStudentId:
self.stuId >= 10000
通过关键字 context 表明这段 OCL 的描述与哪些 UML 对象或类相关。关键字 inv 表示这 是不变的(invariant),也就是说,所有的 Student 对象都要遵守这个约束。关键字 inv 后面的内 容是可选的,表示 OCL 约束的名字,大多数情况下可以省略,但最后的冒号必须存在
eg. 未在休学期的学生必须注册一门课程
context Student::selectedLectures(): Integer
pre stustatus: self.freesemester = false
post selectedLectures: result > 0
关键字 pre 后的内容表示前置条件,其名字为 stustatus,名字为可选内容。前置条件明确了只有当该条件为真时,此约束才会产生作用。
eg. 当该学生选择了一门之前没有学过的课程时,该学生选课总数就加一
context Student::registerLecture(v: Lecture)
pre: not isSelected(v)
post: self.selectedLectures()@pre = self.selectedLectures() – 1
若需要执行之前的状态值,则需要如上述例子中那样,在方法的后面加上 @pre 加以说明
设计优化
设计原则+设计模式
目的:尽可能提升设计方案对变化的适应能力
设计优化思想
类图向程序实现过渡
运行时的多态
多态的基本思想:对类中每个变量,在保证业务成功实现的前提下,尽量保持类型泛化的定义
🌟重写的要求
- 重写的方法的输入范围要包含原输入区间
- 结果输出区间是原输出区间的子集
优点:减少 switch 语句的使用
耦合的消息链
功能方法的实现位置 → 影响对象间交互的复杂程度
改进方法:移动方法的位置,减轻管理类的负担
狎昵关系
表现为高耦合,eg. 某个类高度关注另一个类的私有成员
改进方法:改进借口,该类的业务逻辑拆分出一部分给另一个类
被拒绝的遗赠
在该类的基础上进行功能和结构的拓展
方式
- 继承
- 委托 :新的类与原有类构成关联关系
循环依赖
两个类处在不同的包中,并且具有双向导航的关联关系
解决办法:在包 A 中添加对 B1 的接口 IB1,该接口中含有所有 A1 需要的 B1 方法的定义
设计原则
接口隔离原则(ISP)
- 应尽量使用“接口继承”,而非“实现继承”。接口关注对象的概貌,将对象中“不变”的信息抽象出来,不涉及细节,因此是“稳定”的。
- 通过接口,只将需要的操作“暴露”给需要的类,而将不需要的操作隐藏起来。接口在这里充当类的视图。
依赖倒置原则(DIP)
原则:应依赖于抽象,而不要依赖于具体
开放封闭原则(OCP)
原则:模块对于拓展是开放的,对于修改是封闭的
OCP 是相对的,没有绝对符合 OCP 的设计,而且一个软件系统的所有模块不可能都满足 OCP
Liskov 替换原则(LSP)
原则:任何出现父类的地方都应该能使用子类对其进行无条件的替换
某个类更新后可以替换掉旧的类
做法:不将父类中子类不需要的函数暴露给子类
单一职责原则(SRP)
设计类的功能应该只有一个
合成/聚合服用原则(CARP)
应尽量使用合成/聚合形式的委托重用,尽量不使用继承重用
设计模式
相似的程序设计任务中经常出现的相同问题的解决方案。
分类:
-
创建模式:抽象工厂模式、单例模式
-
结构模式:适配器模式、桥模式、装饰模式、门面模式、代理模式
-
行为模式:观察者模式、策略模式、状态模式
抽象工厂模式
设计一个工厂类,使得 new 操作完全封装在工厂内。
单例模式
类似于静态类,只存在单一的实例
要求:
-
类的所有 构造方法都是私有的
-
提供一个公有的方法来获取实例
-
类中的实例变量是私有的或受保护的
适配器模式
把一个类的接口变换成该类期待的另一个接口
可以通过继承或委托的方式
桥模式
抽象部分与实现部分分离
一个类具有两个维度的属性 → 一个维度用继承实现(抽象部分),另一个用成员变量实现(实现部分)
装饰模式
以对客户端透明的方式拓展对象的功能
桥模式中的“抽象”和“实现”合二为一
门面模式
外部与子系统之间通信必须通过一个统一的门面对象
一般只有一个门面类且是一个单例模式 → 门面 = 适配器
代理模式
一般对有价值的资源进行管理
使实体类分辨不出代理对象与真实资源对象
观察者模式
多个观察者对象同时监听某一个主题对象
对应 MVC 架构:
- 视图 → 观察者
- 模型 → 主题
策略模式
同一问题不同算法,将算法进行抽象
状态模式
策略模式的一种应用,允许一个对象在其内部状态改变时改变其行为
实现技术
重点关注那些与实现相关的关键技术及衍生内容
非功能性需求的实现
硬件方面的需求
原型系统不太注重性能上的表现 → 难以在早期被发现
质量方面的需求
即系统的正确性 → 主要在软件测试阶段处理
安全方面的需求
- 特指授权安全性(Security)
- 数据存储安全性 (Safety)
分布式技术
程序中唯一的控制点 → 分布式系统中多个控制点
进程之间信息交换方式
需要保证原子性 ,即保证,某一事务的执行不会对其他事物产生副作用
eg.转账过程中,扣款和入账操作需要同时成功
临界区:为保证原子性,使某一进程不能被其他进程中断操作的范围
进程间通信的不同形式
远程系统中方法的调用:可通过代理模式实现
远程对象调用的管理
- 每个对象都在其被创建的进程里管理,对对象方法的调用都要进行转发
- 将对象及其副本在网络节点上分布
同步调用和异步调用:信息发出后等待与否
同步调用中,有可能产生死锁
可标记拓展语言 XML
基本原理:将每个领域的数据使用某种 XML 数据格式进行描述,不同领域的数据可自由进行 XML 格式的转换。
XML 的处理方式
-
文档对象模型 (DOM)
XML 比较复杂或需要随即处理文档中的数据
-
用于 XML 的简单 API(SAX)
更为轻巧,不会有大量数据常驻内存中
XML 的实际应用
-
轻量级数据存储
-
数据交换
JSON
XML 文件格式通常比较复杂
JSON 格式在文件传输中更为轻量
程序轮子
对于大多数经常出现的问题,将他们幼稚的解决方法通过函数库或者类库的形式提取出来
组件
组件是一种程序轮子,可以把它理解为一种特殊的对象(不是接口)
组件一般可以工作在 设计时态 和 运行时态 两种模式下
-
设计时态:组件的方法不能被调用,组件不能与最终用户直接进行交互操作,也不需要实现全部功能
-
运行时态:需要对方法的调用进行处理,并实现与其他组件之间有效的协同工作
🌟 组件与系统及组件之间的通信一般是按照观察者模式进行的
Java Bean
条件:
-
必须存在一个默认的构造函数(无参构造)
-
必须存在简单的 get 和与之相符的 set 方法
-
必须实现 Serializable 接口
控制反转与反射机制:
使用 XML 文件来决定一个对象的哪些方法被调用
框架
框架,也称容器,也是一种程序轮子
开发者只需对它进行必要的参数定制,就能够将 其打造成符合用户需求的真实系统
与组件的区别:控制权在框架中要进行转移(框架没有反射机制,只能由框架中的类来调用用户补充实现的方法)
数据持久化
两种方式
-
直接存储于物理文件中
-
通过专门的存储系统对业务数据进行保存,如数据库系统
文件持久化
-
直接使用文件的方式进行数据的持久化
-
若多次存储效率低,可以使用缓存(Java 中的缓存类)
-
实现 Serializable 接口,可以将类作为一个整体进行存储
缺点:文件二进制保存,类发生改变后再次读取会出现问题
数据库持久化
JDBC: DriverManager 建立 Connection 类是通过 抽象工厂 模式实现的
领域特定语言(DSL)
为不同的领域补充特定的、不依赖具体编程语言的抽象指令
- 外部 DSL 是一种“不同于应用系统主要使用语言”的语言
- 内部 DSL 是一种通用语言的特定用法,用内部 DSL 写成的脚本是一段合法的程序
模型驱动架构
模型驱动架构(MDA)的基本思想是提供一种正式的解决方案,其与具体编程语言,甚至是架构无关。
四个阶段
-
CIM(Computation Independent Model):聚焦于系统环境及需求,但不涉及系统内部的结构与运作细节
-
PIM(Platform Independent Model):聚焦于系统内部细节,但不涉及实现系统的具体平台
-
PSM(Platform Specific Model):聚焦于系统落实于特定具体平台的细节,如 EJB、J2EE 或.NET(都是一种具体平台)
-
Coding:最后依据 PSM 的 UML 模型内容,按图施工,编写出适用于特定具体平台的代码
重构
编码规则
- 方法的名字要尽可能地“见名知义”
- 方法最长应不超过 12 行,尽可能少地包含 while、switch 和 if 逻辑块
交互设计
交互设计概述
可用性主要涉及的领域
-
设计心理学
设计心理学中有一项任务是研究颜色和形状带来的影响。颜色和形状的选择总是要结合具体环境进行考虑
除颜色外,形状及其布局在界面设计中也有着重要的影响
-
人体工程学
人体工程学的一部分工作是对工作空间进行设计,其中一项最主要的需求就是工作环境和工作设备要适合工作
要注意软件中的主要功能应易于访问,误操作或错误能够得到很好的捕获,并给出有针对性的提示,使用户不会淹没在信息的海洋中
-
软件人体工程学
软件人体工程学主要是以上提到的想法在实际软件中的实现
可用性
主要考虑广泛使用的 ISO 9241 中的 110 部分
ISO 9241 是关于办公室环境下交互式计算机系统的人体工程学国际标准,由 17 个部分组
任务适合性
交互系统在支持用户完成任务时应适合任务
自我描述性
如果从界面的结构上,就能够清晰地知道什么时间、哪些交互可能发生,为什么发生、会产生哪些可能的结果,那么该界面称为自我描述的界面
可控性
用户能够初始化并控制输入的类型及交互过程的走向、步骤和速度,直到达成目标为止
与用户期望一致性
对话如果与用户可预见的场景需求及普遍沿用的管理保持一致,则称为具有“与用户期望一致性”
容错性
对话的容错性是指即使有错误的输入,在系统错误及其类型的提示下,只要进行很少的修改,就能够得到正确的工作结果
可定制性
交互系统具有根据不同用户的能力和喜好进行设置的能力
易学性
对话的易学性意味着对话应支持和指导用户学习使用该系统
交互设计过程
需求分析
-
主要目标就是识别和了解哪些业务需要在待开发的软件中进行实现
-
另外一项工作做数据的建模,即了解主要业务步骤中都需要哪些信息
-
决定典型终端用户具有的特征
可用性的验证
- 基于领域专家的方法,主要依赖专家的 经验进行评估
- 基于最终用户的方法,通过跟踪和调查最终用户对系统的使用情况进行分析
验证方法
- 启发式评估(专家)
具体的做法一般是将多个专家按照不同的评估 方面进行分组,分别评估,然后将他们的评估结果汇总,形成一份评估报告
- 基准和检查表(专家)
评估对象一般是针对系统的规格说明文档,我们还可以在此基础上设计和利用与可用性相关的检查表进行辅助评估
- 用户调查(用户)
通常提供给用户一份调查问卷,形式上多是一些客观选择题,内容类似于检查表的形式,也可以补充少部分自由回答的问题
- 基于任务的测试(用户)
这些被测试人员将被分配某 些业务任务,并通过目标系统来完成
- 放声思考的测试(用户)——(Thinking Aloud Test)
除了要求记录每个被测试人员的行为,还需要他们说出和解释所做的每个步骤的确切想法
软件测试
质量保证
-
产品保证
- 软件测试
-
过程保证
形式化验证
软件质量 → 软件正确性
不可能用一个单独的程序来判定任意程序的执行是否终止,需要限制需求的类型,才可以进行需求的验证
测试技术
软件测试:使用人工或自动的手段来运行或测定某个软件系统的过程
测试用例:为某个特殊的测试目标 而编制的一组测试输入、执行条件及预期结果,以便测试某个程序路径或核实软件是否满足某个特定需求
测试分类
按照开发的迭代周期分类:
-
单元测试(实现)
-
集成测试(系统设计)
-
系统测试(系统需求)
-
验收测试(客户需求)
根据被测对象被关注的细节程度不同:
-
白盒测试(内部构成细节 → 单元测试)
-
灰盒测试(类,包等程序单元之间的关系 → 集成测试)
-
黑盒测试(系统的外部行为 → 验收测试)
测试策略
回归测试:上一个迭代周期中的测试用例需要在本次的迭代周期的测试中重新执行,以确保没有引入新的缺陷
非功能性测试
性能测试:检查系统是否满足需求规格说明书中规定的性能,检验相关指标能否达标、 是否能够保持
软件度量
软件对质量的量化管理
控制流图
控制流图中的节点表示代码指令,节点间通过有向直线进行连接,表示这些指令执行的先后顺序。 if、switch 和循环指令会在图中对应节点处产生分支。
环形复杂度(McCabe)
降低环形复杂度的方法
- 优化算法结构,使其尽可能简单
- 对其中的 if 结构进行分解,将外层 if 语句包含的程序部分转移到另一个局部方法中
- 利用多态性,使对分支的选择不受到逻辑的控制
方法内聚缺乏度
对类的内聚性进行归一化的度量
式中 m 为方法数;a 为所含的实例变量数;μ(Aj ) 为访问每个实例变量的方法数。
等价类测试
寻找测试用例
等价类方法
等价类具有完全性和不相交性
等价类与边界
缺陷经常会在边界处发生,等价类的边界应该得到格外的关注
等价类组合
强等价类方法:将多个变量的多个等价类分别进行组合
弱等价类方法:不采用组合覆盖的方式,而是要求每个等价类只覆盖一次即可
面向对象中的等价类
对象作为一个有机的整体,具有静态的属性和动态的方法
首先要确定类的状态集合,然后据此进行等价类的划分
类的状态是由其静态属性确定的,即实例变量
基于控制流的测试
对于开发者来说,通常需要 了解程序内部的结构,如选择某种特定的循环和分支结构来 实现与业务逻辑之间对应的正确性,把基于这种思想的测试称为 白盒测试
白盒测试的思想就是充分利用程序的结构信息设计测试用例,以实现对每个程序块(代码)的覆盖
测试覆盖分析是指提供一组测试用例,尽可能使覆盖率指标越大越好,更准确地说,覆盖率越接近 1 越好
- 语句覆盖
语句覆盖表示在程序控制流图中测试经过的节点数与所有节点数的比例
- 分支覆盖
尽可能覆盖控制流图中所有的边
- 条件覆盖
要求每个原子谓词的真假两种取值都要取到,即条件覆盖
- 多条件组合覆盖
所有在条件中出现的原子谓词的 组合 都要覆盖到
- 路径覆盖
所有可能路径的覆盖情况
断言机制
一般形式为 assert
🌟 只能在编码阶段使用,并且不能替代常规的异常处理
断言的主要作用是在某些关键的位置确认某种假设在程序中是正确的
测试框架
提供了一种对测试进行自动化管理的环境
测试环境
测试用例来自已经建立的规格说明书
规格说明源自用户需求
-
测试环境的搭建应该与未来用户的使用环境情况越相近越好
-
测试的过程也需要进行精确的描述
-
测试用例中需要精确地给出期望的运行结果
JUnit
目前 JUnit 的最新版本 JUnit 5,但 JUnit 4 目前应用得更为广泛,它是在 JUnit 3 的基础上主要增加了对 Java 5 及以上的支持,即加入了 Java 的 Annotation 机制
JUnit 的基本思想是对不同的测试用例创建与其对应的测试方法,测试用例的执行和评价由 JUnit 接管。
设置测试的顺序以避免不同测试的结果造成的影响
protected void setUp(){ ... }
//setUp()方法在每个测试开始之前都要执行
protected void tearDown(){ ... }
//每个测试结束后会执行 tearDown()方法,它比较适合做一些清理工作
总结一下,测试方法 testX()的执行首先是 setUp()先执行,然后是 testX()本身执行,最后是 tearDown()执行
在很多大型的项目开发中,需要对测试用例进行收集和管理,并在合适的时刻重复执行 所有测试用例或者根据情况重复执行部分测试用例,这称为 回归测试 —— JUnit TestSuite 工具。
可测试性
类的桩或模拟 (Stub or Mock):开发者开发的 A 类需要依赖其他开发者的开发成果 B 类,但 B 类还没有正式开发完毕时构建 B 类的一个简化形式的 B’类来模拟 B 类。
可测试性构建原则
-
设计简单的方法
带有很多规模较小的方法的类的测试性要好于那些带有较少方法但每个方法的规模较大的类
getRent(car, customer)
getRent(car.getDays(), car.getPrice(), customer.getDiscount())
//第二个方法优于第一个
- 避免私有方法
- 优先使用通用方法
- 组合优于继承
- 避免隐藏的依赖与全局状态
建设性质量保证
分析式的质量保证方法(Analytical QA):在软件开发后作用于 测软件,用于发现缺陷的存在
建设性质量保证活动(Constructive QA):实际产品开发完成之前的质量活动
人工测试
审查(Inspection)
审查活动的重点在于整个过程中对资源的计划,因为正式的分析过程需要耗费人员大量的时间和精力
产品作者通常不参加审查会议
评审(Review)和走查(Walkthrough)
评审与审查比较起来主要的差别在于准备工作没有那么正式
走查中作者作为主持人主持整个讨论过程,作为会议的主导
软件项目级管理
软件的管理可以分为两个层面,一是项目级管理,二是组织级管理
项目及管理主要包括:配置管理、软件计划、软件质量保证、风险管理、人员与沟通等
软件配置管理
软件配置管理(Software Configuration Management,SCM) 是一种标识、组织和控制修改的技术,贯穿于整个软件生命周期。
- 核心部分是版本(Version)管理
- 构建(Build)管理能够提供产品之间依赖的路线
- 发布管理(Release)的主要作用是协调在合适的时间对 合适的用户交付合适产品
- 变更管理(Management of Change,MoC)
版本管理
- 规范化不同开发者之间的合作方式
- 确保每个人的工作内容是当前需要的版本
版本仓库(Repository):存储所有项目内容的数据库
所有纳入版本仓库进行管理的各种软件资产统称为软件配置项
每个配置项都有一个独立的版本历史,称为代码线(Codeline)
基线(Baseline):通过审查的软件配置项
主线:一组顺序的基线构成的集合
代码线(Codeline):每个配置项都有的一个独立的版本历史
二区结构
- 检出 (Check Out): git clone/git fetch
- 检入 (Check In): git commit + git push origin
解决冲突的两个方法:
- 悲观的方法:某一开发者正在编辑时,其他开发者无法检出
- 乐观的方法:参考 git branch 功能
构建管理
主要任务是描述最终软件产品的结构和生成过程
调用编译器和链接器生成可执行文件的过程
项目管理
项目管理主要关注组织和管理层面的内容
项目计划与工作分解
项目经理:负责项目在整体上的计划、进度控制等工作 + 领导地位
管理为主——项目领导,技术为主——技术领导
🌟 工作分解结构(Work Breakdown Structure,WBS):将任务按照层次的结构从上到下逐步进行分解
-
按照任务层次的结构 从上到下 进行分解
-
子任务间的工作内容应尽量做到无重叠或较少的重叠
-
每个工作包应存有两个评估值 —— 期望的工作量和为潜在问题预留的缓冲量
软件规模估算
传统的软件规模估算方法是一种基于分解的方法,项目可以按照 WBS 的方式分解为子项目,由一个或多个专家基于他们的经验对各子项目的工作量分别进行评估
-
代码行估计
-
功能点估计
开发成本估算
模型 CoCoMo(Constructive Cost Model) :利用功能点技术来对成本进行估算的方法
任务安排和工程网格图
工作任务之间的依赖关系可以通过图形的方式进行展现
工程网络图的计算
- 前向计算:处理最早的时间
- 后向计算:处理最晚的时间
项目组织和甘特图
项目计划要能够方便、直观地将相关信息确定并展示出来,包括项目中各任务的时间、人力等 资源安排,而且要明确各任务的主要责任人
项目计划经常使用一种称为甘特图(Gantt Chart)的图形来对计划进行清晰的描述,包括各 工作任务的完成进度、人员分配等详细信息
- 内部里程碑:主要是在开发团队内部进行的进度评审
- 外部里程碑:客户需要在此了解当前项目的进展并对产品进行部分验收
项目计划跟踪控制
项目计划跟踪
项目经理的一个重要任务是随时掌握项目当前的进行状态,识别出潜在的风险或延期的 征兆,并快速应对
- 已进行的工作量
- 任务完成进度
项目进行中的所有决定都需要在项目日志中进行记录,而且应由项目经理在项目结束后给出一个最终的项目总结评价
挣值分析
挣值分析(Earned Value)是对项目实施的进度、成本状态进行绩效评估的有效方法
输入
- BCWS(Budgeted Cost of Work Scheduled,计划完成工作的预算成本)
- ACWP(Actual Cost of Work Performed,已完成工作的实际成本)
- BCWP(Budgeted Cost of Work Performed,已完成工作的预算成本):又称挣值
- BAC(Budgeted At Completion,工作完成的预算成本)
输出
- 进度偏差(Schedule Variance,SV)= BCWP−BCWS
- 成本偏差(Cost Variance,CV)= BCWP−ACWP
- 进度执行指标(Schedule Performance Index,SPI)= BCWP/BCWS
- 成本执行指标(Cost Performance Index,CPI)= BCWP/ACWP
50/50 规则是指当一项工作任务已经开始,但是没有完成时,假定已经实现 50%的价值,当这个工作任务全部完成的时候, 才实现全部的价值
0/100 规则是指当一项工作任务开始,但是没有完成时,不产生任何价值, 即价值是 0,直到完成时才实现全部的价值
项目偏差控制
需要设定一个允许偏差范围,对超出允许偏差范围的挣值分析结果,需要采取措施纠正偏差
软件质量保证
质量管理
软件项目的质量管理是指,保证项目满足其目标要求所需要的过程,质量管理的关键是 预防重于检查,事前计划好质量,而不是事后检查
主要过程
- 软件质量规划 (Software Quality Planning):确定与项目相关的质量标准及如何满足这些标准
- 软件质量保证(Software Quality Assurance,SQA):通过定期的评估项目的整体性能,确保项目满足相关的质量标准
- 软件质量控制(Software Quality Control):控制特定项目的状态,保证项目完全按照质量标准完成,同时确定质量改进的方法
软件质量保证的内容
软件质量保证的内容主要包括:软件质量保证过程;具体的质量保证和质量控制任务(包 括技术评审和多层次测试策略);有效的软件工程实践(方法和工具);对所有软件工作产品及 其变更的控制;保证符合软件开发标准的规程;测量和报告机制。
- 标准:IEEE,ISO制定
- 评审和审核:技术评审是由软件工程师执行的质量控制活动,目的是发现错误。审核 是一种由软件质量保证人员执行的评审,意图是确保软件工程工作遵循质量准则
软件质量保证的任务
CMMI(Capability Maturity Model Integration,软件能力成熟度模型集成)的质量保证活动任务
- 编制项目质量保证计划
- 参与编写项目的软件过程描述
- 评审软件工程活动,以验证其是否符合规定
- 审核指定的软件工程产品,以验证是否遵守规定
- 确保根据文档化的规程,记录和处理软件工程和工程产品中的偏差,包括在项目计划、 过程描述、适用的标准或软件工程工作产品中存在的偏差
- 记录各种不符合项并报告给上层管理人员
软件质量保证计划
该计划由软件 质量保证团队来制订,作为各个软件项目中软件质量保证活动的模板
风险管理
相比起被动风险策略,风险管理的一个更好的策略是主动策略。
- 风险识别:一般在项目开始时就已经进行了,越早发现风险,后续的步骤就越容易
- 风险分析:风险出现的概率有多大+一旦出现风险,其破坏程度如何
- 措施计划:对每个威胁的风险制订最小化风险管理过程其危害的应对计划
- 措施执行:按照措施计划执行应对措施
- 结果评估:措施执行后规定的时间点,依据每项措施的评估指数 验证该措施是否成功
- 优化:对某项风险及其应对措施的优化
- 风险数据库:组织级上建立的经验数据库
项目人员构成与沟通
项目人员构成
项目的整体视图
- 过程层面:包括企业中所有的过程定义及其描述。
- 能力:包括所有工作人员能够掌握和应用的所有技术和构件。
- 社交层面:包括工作人员之间所有的交互情况、交互特点及交互质量。
- 工作环境:包括所有组织级的能够影响项目成败的制度和设施。
项目的整体视图指出,一个领域的问题是不会在另外的领域中被解决的
- 组建(Forming)
- 风暴(Storming)
- 规范(Norming)
- 行动(Performing)
将项目人员的类型按照特点划分为 9 类
- 专家(Specialist)
- 润饰者、完工者(Completer、Finisher)
- 实现者(Implementer)
- 团队者(Team Worker)
- 监督评估者(Monitor Evaluator)
- 领导者(Shaper)(开发任务)
- 协调者(Coordinator)(项目社交)
- 资源投资者(Resource Investigator)
- 电厂(Plant)
项目人员沟通
每条消息都由四个方面构成,但每个方面强调的重点有所不同
-
事实(Fact)
Eg
“你应对这个类进行注释” → 负责开发这个类的人还没有对它进行注释”
-
自我揭示(Self-Revealing):“我揭示自己什么”
-
关系 (Relationship):对话的双方对待彼此的态度
-
诉求(Appeal):“我想从你那里获得什么”
软件过程管理与改进
对如此复杂的开发与维护过程,组织必定要有能力进行管理并控制,所以,有效的组织级管理是成功的关键
一般认为,CMMI/TSP/PSP 是目前世界上最好的软件过程管理的模式之一,CMMI 提供 了框架和目标,PSP 针对个人进行优化,TSP 针对团队进行优化
软件过程管理
过程改进
质量的基本目标:提供给客户满意的产品
产品向客户的最终交付:质量控制
能力成熟度模型
能力成熟度模型(Capability Maturity Model,CMM)具有很多的变种,最后统一起来形成 CMMI。
CMM 过程域
初始级 → 可重复级 → 已定义级 → 已管理级 → 优化级
-
初始级(Initial)
开发的初级阶段,没有引入任何系统化的过程控制
-
已管理级(Managed)
引入了关键的子过程,仍然采用被动的问题应对方式
-
已定义级(Defined)
所有已有的过程都进行了统一的文档化,它们能够被理解和利用,并为其他项目提供一 个统一的框架。采用主动问题应对方式
-
已量化管理级(Quantitatively Managed)
对过程和产品质量进行量化的度量,组织级过程性能(OPP)和量化项目管理(QPM)
-
优化级(Optimizing)
组织级革新与实施(OID)、原因分析与解决(CAR)
CMMI 二级及以上的子过程又称过程域(Process Area) 过程域具有一系列的目标, 包括特定目标(Specific Goals)和通用目标(General Goals),并给出了它们对应的特定实践 (Specific Practice)和通用实践(General Practice)
CMMI 过程域(Process Area, PA)
做好软件开发的某一个方面
CMMI 中过程域(PA)的主要内容分为四大类 22 个,其中 2 ~ 3 级有 18 个, 4 ~ 5 级有 4 个
-
过程管理类
都带有 “组织” 二字
-
项目管理类
除 “风险管理”、“集成项目管理”、“需求管理” 之外都带有 “项目” 二字
-
工程类
需求开发、技术解决方案、产品集成、确认、验证
-
支持类
配置管理、过程和产品质量保证、测量与分析、决策分析与解决
个体软件过程(Personal Software Proces,PSP)
- PSP0 能够理解软件过程的思想并且完成相应的开发工作
- PSP1 总结总体工作时间,对出现的缺陷进行度量
- PSP2 要求开发者能够树立自己的质量目标并通过自我评审评估工作效率
团队软件过程(Team Software Process,TSP)
TSP ← 能力领域 ← 知识域 ← 概念和技能
参考资料
- 《软件工程》 朴勇
- 《软件工程导论》 张海藩
- 大连理工大学软件学院 软件工程课程 课件 朴勇
⌛️ Last Modified: 07:49:26 17 May 2024 UTC