软件工程导论
软件工程绪论
软件定义
- 1983年IEEE的软件定义:计算机程序、文档、运行程序必须的数据、方法、规则。方法和规则在文档中说明,在程序中实现
- 简化软件定义:程序+文档+数据
软件分类
- 基于软件功能的划分: 系统软件、支撑软件、应用软件
- 系统软件:与计算机硬件机密配合使计算机各个部件与相关软件及数据协调、高效工作的软件,如操作系统、编译程序等
- 支撑软件:协助用户开发的工具性软件,Rational Rose、Power Designer、青鸟系统(北大)等
- 应用软件:
- 商业数据处理软件1
- 工程与科学计算软件CAD/CAM(计算机辅助设计/制造)仿真模拟、天气预报等
- 智能产品嵌入式软件:驻留在智能产品内存,控制产品工作的软件,如家用电器、智能手机等
- 人工智能软件:利用非数值算法去解决复杂问题的软件,如专家系统、模式识别系统、人工神经网络软件等
- 个人计算机软件:文字处理系统、电子表格、游戏娱乐软件等
- 基于软件规模的划分:微型、小型、中型、大型、甚大型、极大型
类别 | 参加人员 | 开发周期 | 产品规模(LOC) |
---|---|---|---|
微型 | 1 | 1~4周 | 0.5k |
小型 | 1 | 1~6月 | 1k~2k |
中型 | 2~5 | 1~2年 | 5k~50k |
大型 | 5~20 | 2~3年 | 50k~100k |
甚大型 | 100~1000 | 4~5年 | 1M(=1000k) |
极大型 | 2000~5000 | 5~10年 | 1M~10M |
软件发展
- 程序设计阶段:
- 软件生产的个体化:规模小,编写者和使用者同一人
- 软件是设计者头脑中隐含的过程:除程序清单,无文档材料保存
- 程序系统阶段:
- 产品软件:有专门的软件开发组织开发
- 软件作坊:软件开发组织,沿用早期个体化软件开发方法
- 软件危机:软件维护工作耗费大量资源
- 软件工程:1968年,北大西洋公约组织计算机科学家讨论软件危机,提出软件工程
- 软件工程阶段:
- 软件危机得到缓解:打破软件生产个体化特征;工程化的设计原则、方法和标准
- 新的挑战:微处理器和个人计算机;分布式处理系统;局域网、广域网;宽带数字通讯
- 第四阶段:
- 客户机/服务器(早期):胖客户瘦服务器
- 客户机/服务器(三层):客户端、应用服务器、数据库服务器
- 浏览器/服务器(b/s):客户端、Web服务器、数据库服务器
- 新技术:专家系统、人工智能、神经网络、并行计算、网格技术、高信度计算、云计算
软件危机
软件危机定义:在计算机软件开发和维护过程中遇到的一系列严重的问题
软件危机主要表现:
- 开发成本和进度估计不准:延迟交付、取消项目
- 用户对已交付软件不满意:开发人员对用户信息交流补充分,产品不符合用户需求
- 软件产品质量靠不住:软件产品保证技术(审查、复审、测试)未坚持不懈应用软件开发全过程
- 软件可维护性差:开发时未考虑,很多错误难以改正
- 软件没有适当的文档资料:文档资料应在软件开发过程中产生,保证最新
软件工程的定义
1993年IEEE的定义:把系统化、规范化、可度量的途径应用于软件开发、运行和维护过程中;研究其实现途径
软件工程内容
- 软件工程技术:软件开发方法学、软件开发过程、软件工具和软件工程环境
- 软件工程管理:软件管理学、软件经济学、软件心理学
软件生存周期
软件从产生、发展到成熟、直到死亡为止
软件生存周期定义:国标《计算机软件开发规范》,分8个阶段
- 可行性研究与计划:
- 关键任务:解决问题是什么?有行得通解决方法?粗略计划
- 问题定义报告:问题性质、工程目标、工程规模
- 可行性研究报告:从经济、技术、社会(操作)角度对可行性进行研究
- 项目开发计划:粗略
- 需求分析:
- 关键任务:目标系统必须做什么?可行性研究的需求分析是粗略、不准确;需求分析是完整、准确、清晰具体
- 需求规格说明书:目标系统需求
- 总体设计:
- 关键任务:怎样实现目标系统?根据需求设计方案,分析推荐最佳方案,设计软件结构等
- 总体设计说明书:记录总体设计结果
- 详细设计:
- 关键任务:该怎么样具体实现系统?设计每个模块的算法和数据结构
- 详细设计说明书:用适当表达工具表达算法和数据结构
- 实现(编码和单元测试):
- 关键任务:选择语言、工具翻译详细设计结果、测试模块
- 实现阶段文档:程序清单、单元测试报告
- 集成测试:
- 关键任务:将经过单元测试模块组装起来进行测试;通过测试使软件达到预定要求
- 测试报告:测试计划、测试方案、测试结果
- 确认测试:
- 关键任务:由用户按需求规格说明书规定进行测试
- 测试报告:测试计划、测试方案、测试结果
- 使用和维护:
- 关键任务:通过必要维护活动使系统持久满足用户要求
- 维护类型:
- 改正性维护:软件运行过程中发现错误,进行维护
- 适应性维护:软件运行软硬件环境变化,进行维护
- 完善性维护:用户要求改进或扩充软件,进行维护
- 预防性维护:为将来的维护做准备
软件过程模型:实际从事软件开发工作时,软件规模、类型、开发环境及技术方法等因素会影响到阶段划分,及各阶段的执行顺序,形成不同生存周期模型,又称过程模型
常用软件过程模型
- 瀑布模型:使用最早、应用最广
瀑布模型的特点:
- 阶段具有顺序性和依赖性:前一阶段结束后一阶段开始,前一个阶段输出文档,后一个阶段输入文档
- 推迟实现的观点:瀑布模型在编码前设置系统分析、系统设计,推迟程序物理实现,保证前期工作扎实
- 质量保证观点:瀑布模型每个阶段坚持两个重要做法:一是每个阶段必须完成完整、准确的文档。软件开发时人员间通信、运行期维护的重要依据;二是每个阶段结束前对文档评审
优点:提高软件质量,降低维护成本,缓解软件危机
缺点:模型缺乏灵活性,无法解决需求不明确问题。用户不经过实践提出完整准确需求不切实际
- 快速原型模型:快速建立反映用户主要需求的原型系统,反复由用户评价修改需求,开发出最终产品
优点:确定需求上优于瀑布模型(通过原型与用户交互);提供学习手段,通过开发原型和演示原型对开发者和使用者了解系统都有积极作用;有的软件原型可以成为最终产品的一部分
缺点:快速建立的系统结构加连续修改可能导致产品质量低下,原型系统的内部结构可能不好
- 增量模型:又称渐增模型,开发软件是将软件产品作一系列增量构件设计、编码、集成和测试
区别于瀑布模型和快速原型模型:瀑布模型和快速原型模型是一次把满足所有需求产品提交给用户;增量模型是分批向用户提交产品
优点:较短时间向用户提交可完成有用工作产品;用户有充裕时间学习适应产品;软件结构必须开放,方便向现有产品加入新构件
缺点:做到第三个优点较为困难
前述增量模型在实现构件前完成总体的需求分析、规格说明和概要设计,相对来说风险较小
风险更大增量模型:确定用户需求后,各构件集并行构建
- 螺旋模型:1988年提出,加入了风险分析,常指导大型软件项目
软件风险:超期、超预算、行业竞争等
笛卡尔坐标四象限表达四方面活动:
- 指定计划:确定目标、选定方案、设定约束条件
- 风险分析:评估方案,识别和消除风险
- 实施工程:软件开发
- 客户评估:评价开发工作,计划下一阶段工作
沿螺线自内向外每旋转一圈开发出更完善新版本
优点: 大型软件开发项目有较好的风险控制
缺点:需要风险评估的经验;契约开发通常需要事先指定过程模型和发布产品,普及不如前述模型
- 喷泉模型: 面向对象生命周期模型,体现了迭代和无缝特性
迭代:求精,系统某部分常被重复工作多次,相关功能在每次迭代中逐渐加入演进系统
无缝:分析、设计、编码各个阶段间不存在明显边界
优点:无缝,可同步开发,提高开发效率,节省开发时间,适应面向对象软件
缺点:可能随时加各种信息、需求与资料,需要严格管理文档,审核的难度加大
- Rational统一过程:由Rational软件公司推出的一种软件过程,该过程强调以迭代和渐增方式开发软件
Rational统一过程是一个二维生命周期模型
RUP有9个核心工作流,包括6个核心过程工作流和3个核心支持工作流
RUP有4个连续阶段,每个阶段有明确目标,通过一次或多次迭代完成
优点:不断的版本发布成为一种团队日常工作的真正驱动力;将发现问题、制定方案和解决过程集成到下次迭代;迭代开发,降低风险;更好地安排产品开发的辅助过程
- 微软公司软件开发过程:
规划阶段:开展市场调查研究,结合公司战略形成产品的远景目标
设计阶段:根据产品远景目标,完成软件功能规程说明和总体设计,确定产品开发的主要进度
开发阶段:完成产品中所有构件的开发工作
稳定阶段:实行全面的内部测试和外部测试,最终形成可发布的RTM版本
发布阶段:确认产品质量符合发布标准后,发布产品及相关消息
递进式开发策略:解决问题的及时性、不确定和变更因素可控性、缩短产品上市周期
结构化软件开发方法
结构化分析概述
需求分析的重要性
对软件需求深入理解是开发成功的前提和关键
Frederick Brooks在1987年经典文章“No Silver Biillet”中阐述需求重要性:开发软件系统最困难的部分就是准确说明开发是什么,最困难的概念性工作是编写出详细需求,包括所有面向用户、面向机器和其他软件系统的接口
此工作一旦做错,将会给系统带来极大损害,并且以后对它修改也极为困难
结构化分析核心思想
面向数据流需求分析方法,20世纪70年代末E.Yourdon提出,应用广泛
核心思想:
- 分解化简问题
- 物理与逻辑表示分开
- 进行数据与逻辑抽象
结构化分析具体步骤:
发现需要求
- 与用户交谈,向用户提问题
- 参观用户的工作流程,观察用户的操作
- 向用户群体发调查问卷
- 与同行、专家交谈,听取他们的意见
- 分析已经存在的同类产品,提出需求
- 从行业标准、规则中提取需求
- 从Internet上搜查相关资料等
求精
- 对初步需求反复求精多次细化
建模
- 建立模型,用图形符号和组织规则书面描述事物
模型核心:数据字典
描述软件使用和产生的所有数据对象
数据模型:E-R图表达
描述数据对象间关系
图中数据对象属性用“数据对象描述”表达
功能模型:DFD表达
描绘数据在软件中移动、变换及相应功能
图中功能用“处理规格说明“表达
行为模型:状态转换图
描绘系统状态和在不同状态间转换方式
图中软件控制附加信息用“控制规格说明”表达
规格说明
- 书写软件需求规格说明书,作为分析阶段最终成果
复审
数据模型
数据对象 :
- 软件必须理解的复合信息表示,复合信息是具有一些列不同性质或属性的事物
- 事物(报表)、地点(仓库)、角色(教师、学生)、单位(会计科)、行为(打电话)等
数据对象间关系:
- 对象彼此间相互连接方式,也称联系
- 教师和学生间存在“教”的联系
- 分三类,1:1(一对一)、1:N(一对多)、M:N(多对多)
属性:
- 定义数据对象性质
- 数据对象学生的属性可为学号、姓名、班级等
表示数据模型最常用的图形工具:实体-关系图
- 实体用矩形框
- 属性用圆角矩形
- 关系用菱形
E-R图用简单符号表达分析员对问题域理解,作为用户与分析员有效交流工具
功能模型
数据流图(DFD)描绘系统逻辑模型,图中没具体的物理元素,只描绘信息在系统中流动处理情况。是非常好通讯工具和软件设计出发点
数据流图符号
四种基本符号:
正方形(或立方体):表示数据的源点或终点
圆角矩形(或圆形):代表变换数据的处理
一系列程序、单个程序或程序一个模块;人工处理过程
开口矩形(或两条平行横线):代表数据存储
文件、文件的一部分、数据库元素或记录一部分,可存在磁盘、磁带、磁鼓、主存、微缩胶片任何介质上
箭头:表示数据流,即特定数据的流动方向
在处理之间有向流动的数据项或数据几何
附加符号:
- A * B -> C:数据A和B同时输入才能变换成数据C
- A -> B * C:数据A变换成B和C
- A + B -> C:数据A或B,或B和C同时输入才能变换成数据C
- A -> B + C:数据A变换成B或C,或B和C
- A ⊕ B -> C:只有数据A或只有数据B(但不能A、B同时)输入是变成C
- A -> B ⊕ C:数据A变换成B或C,但不能变换成B和C
分层数据流图
- 为表达数据加工情况,需采用层次结构数据流图。顶层数据流图包含一个加工项
- 底层流图指加工项不再分解的数据流图
- 中间层流图只在顶层和底层之间,对其上层父图的细化
分层法绘制流程图的问题:
编号的设置
子图的编号是父图相应的处理逻辑的编号
子图中处理逻辑编号由子图号、小数点与局部号组成
父图与子图的平衡
子图详细地描述父图中处理逻辑
子图的输入、输出数据流应同父图处理逻辑的输入、输出数据流相一致
局部数据存储
在子图中出现的数据存储,可以不出现在父图中,画父图时只需要处理逻辑之间的联系,不必画出各个处理逻辑内部的细节。数据存储可以随数据流逐层增加
数据流图命名规则:
数据流(数据存储)命名
- 用名词,区别于控制流
- 代表整个数据流(数据存储)内容,不仅仅反映某些成分
- 不用缺乏具体含义名字,如“数据”、“信息”
处理命名
- 用动宾词组,避免使用“加工”、“处理”等笼统动词
- 应反映整个处理的功能,不是一部分功能
- 通常仅包括一个动词,否则分解
数据源点/终点
不属于数据流图的核心内容,可能是人员、计算机外部设备或传感器装置。采用它们在问题域中习惯使用的名字(如:“采购员”、“仓库管理员”等)
数据流图用途
作为交流信息的工具
作为分析和设计的工具
用数据流图辅助物理系统设计时,可在数据流图上画出许多组自动化边界,每组自动化边界可能意味着不同的物理系统
行为模型
软件的行为模型:状态、事件、行为
状态:被观察到的系统行为模式
事件:引起状态转换的外界事件抽象
行为:进入某状态所做的动作
状态装换图
状态:
- 初始状态:由实心圆点表达
- 最终状态:由空心圆圈内套实心圆点表达
- 中间状态:由圆角矩形(内部分为:状态名、状态变量、行为)表达
事件:由箭头表达,箭头上标事件名。后跟[条件],表状态转换条件
行为:状态框内加do:行为名
数据字典
对于系统使用的所有数据元素定义的集合,半形式化方法表达
数据字典定义方法
数据字典对四类元素定义:数据流、数据元素、数据存储、处理
数据流的描述:
数据流名:
说明:简要介绍作用即它产生的原因和结果
数据流来源:该数据流来自何方
数据流去向:该数据流去向何处
数据流组成:数据结构
数据量、流通量
数据元素的描述:
数据元素名
类型:数字(离散值、连续值),文字(编码类型)
长度:
取值范围:
相关的数据元素及数据结构:
数据存储的描述:
- 数据存储名:
- 简述:存放什么数据
- 输入数据:
- 输出数据:
- 数据文件组成:数据结构
- 存储方式:顺序,直接,关键码等
- 存取频率:
处理的描述:
- 处理名:
- 处理编号:反映该处理的层次
- 输入数据流:
- 输出数据流:
- 加工逻辑:简述加工程序、加工顺序
数据字典定义符号
定义数据方法:对数据自顶向下分解
有数据元素组成数据的方式:
- 顺序:以确定的次序连接两个或多个数据元素
- 选择:从两个或多个可能元素中选一个
- 重复:把指定数据元素重复零次或多次
- 可选:一个数据元素可有可无的
符号 | 含义 | 举例 |
---|---|---|
= | 等价于/定义为 | x=a |
+ | 与/连接 | x=a+b |
[|] | 或/选择 | x=[a|b] |
m{ }n | 重复m..n次 | x=1{a}5 |
( ) | 可选 | x=a+(b) |
“ “ | 基本数据元素 | x=”0” |
.. | 范围 | x=”1”..”9” |
结构化设计
结构化设计的概念与原理
传统的软件工程方法学采用结构化设计技术(SD)。从工程管理角度结构化设计分两步:
- 概要设计:将软件需求转换为数据结构和软件系统结构
- 详细设计:过程设计,通过对结构细化,得到软件详细数据结构和算法
数据设计:数据模型及核心数据字典转换为数据结构
体系结构设计:功能模型中数据流图转变成计算机模块框架
接口设计:功能模型中数据力流图转变成软件内部、软件与协作系统间、软件以用户间通讯方式
过程设计:行为模型及功能模型中的“处理规格说明”转换成软件构件过程描述
模块化
模块(module):“模块”又称“构件”一般指用一个名字调用的相邻程序元素序列
模块化设计(module design):按适当的原则把软件划分为一个个较小的、相关而又相对独立的模块
设:C(x)为复杂程度函数
E(x)为决定解决问题x所需的工作量(时间)函数
C(P1) > C(P2) E(P1) > E(P2)
C(P1 + P2) > C(P1) + C(P2) E(P1 + P2) > E(P1) + E(P2)
该公式忽略了模块之间接口规模对开发成本的影响,在实际生活中并不是模块越多成本越低
抽象
Miller法则中介绍:奇妙的数字7±2,人类信息处理能力的限度
抽出事物的本质特性,暂不考虑细节
求精
求精是指为了能集中精力解决主要问题,尽量推迟对细节问题的考虑,实际上是一个细化过程,与抽象是互补的概念
抽象是的设计者能够说明过程和数据,同时却忽略底层细节
求精帮助设计者在设计过程中揭示底层细节
信息隐藏
每个模块的实现细节对于其他模块来说是隐藏的。也就是说模块所包含的信息是不允许其他不需要这些信息的模块访问的
每个客户只能通过接口来解决该模块,而所有的实现都隐蔽起来
模块独立
具有独立功能且和其他模块没有过多作用
两条理由:
- 容易分工合作
- 容易测试和维护,修改工作量较小,错误传播范围小,扩充功能容易
两个定性度量标准:耦合和内聚
模块独立(耦合、内聚)
耦合
定义:软件结构中不同模块间互联程度度量
取决于模块间接口复杂程度,通过接口数据。追求尽可能松散的耦合系统
无耦合—没有依赖关系(最理想状态)
松散耦合—有少量依赖关系(追求)
紧密耦合—有很多依赖关系
- 非直接耦合:两个模块分别能独立工作不需要另一个模块存在
- 数据耦合:两模块通过参数交换数据信息(相对松散的耦合)
- 控制耦合:两模块通过参数交换控制信息(包括数字形式)(相对紧密的耦合)
- 公共环境耦合:两个或多个模块通过一公共环境作用
- 一个模块送数据,另一个模块取数据,等价数据耦合(相对松散的耦合)
- 两个模块即往公共环境送又从公共环境取,介于数据耦合和控制耦合之间
- 内容耦合:(非常紧密的耦合,在设计中尽量出现)
- 一个模块访问另一个模块内部数据
- 一个模块不通过正常入口转到另一个模块内部
- 两个模块有部分程序代码重叠(汇编程序)
- 一个模块有多个入口
原则:尽量使用数据耦合,少用控制耦合,限制公共环境耦合,完全不用内容耦合
内聚
定义:模块内各元素彼此结合紧密程度
- 功能内聚:一模块中各部分是完成某一功能必不可少组成部分(高内聚)
- 顺序内聚:模块内处理同某功能密切相关,顺序执行(高内聚)
- 通讯内聚:一模块内各功能部分都使用相同输入数据,或产生相同输出数据(较高内聚)
- 过程内聚:模块内处理元素相关,特定次序执行。如把流程图中循环部分、判定部分、计算部分分成三个模块,这三个模块都是过程内聚模块(中等内聚)
- 时间内聚:多为多功能模块,要求所有功能在同一时间内执行,如初始化模块和终止模块及紧急故障处理模块 (较低内聚)
- 逻辑内聚:一模块完成功能在逻辑上属于相同相似一类
- 偶然内聚:模块内部分间没有联系,及时有也很松散
对不同内聚打分:功能内聚:10分;顺序内聚:9分;通讯内聚:7分;过程内聚:5分;时间内聚:3分;逻辑内聚:1分;偶然内聚:0分
启发规则
- 改进软件结构提高模块独立性
初步结构分解或合并,降低耦合提高内聚
- 模块规模应该适中
过大分解补充分,但是进一步分解不应降低模块独立性
过小开销大于有效操作,模块数目过多系统接口复杂
通常语句行数在50~100(一页纸),最多不超过500行
- 深度、宽度、扇出和扇入因适当
深度:软件结构控制层数,标志一系列大小和复杂程度
宽度:软件结构同一层模块数最大值,越大系统越复杂
扇出:一个模块直接控制(调用)模块数。过大,模块负责;过小,不好。一般控制在3-9
扇入:有多少个上级模块直接调用它,越大共享该模块上级模块越多
- 模块作用域应在控制域内
作用域:受该模块内判定影响的所有模块集合
控制域:模块本身及所有直接或间接从属它的模块集合
若模块作用域不在控制域内,会增大模块间控制耦合
- 降低模块接口复杂程度
模块接口复杂是软件发生错误主要原因。应使信息传递简单且和模块功能一致
QUAD-ROOT(TBL,X) QUAD-ROOT(A,B,C,ROOT1,ROOT2)
- 设计单入口、单出口模块
避免内容耦合
- 模块功能可预测
输入数据相同,产出同样输出,模块功能能防止过分受限
面向数据流设计方法
面向数据流的设计方法要解决的任务,就是将软件需求分阶段生成的逻辑模型数据流图映射(Mapping)表达软件系统结构的软件结构图
结构化设计属于面向数据流的设计方法
软件结构图
- 模块—在SC图中用矩阵框表示,并用名字来标记它
- 模块的调用关系和接口
信息流类型
变换流
信息沿输入通路进入系统,由外部形式变换成内部形式,通过变换中心加工处理后,再沿通路变换成外部形式离开软件系统
事务流
信息沿输入通路到一处理,由处理根据输入信息类型在若干动作序列中选一个执行
处理程事务中心,完成任务:
1)接收输入信息(又称事务)
2)分析每个事务确定类型
3)根据事务类型选取一活动通路
面向数据流设计过程
变换分析
将具有变换流特点的数据流图映射成软件结构
- 复查基本系统模型:确保系统输入和输出数据符合实际
- 复查并精化数据流图:正确、处理项完成相对独立功能
- 确定数据流图有变换特性还是事务特性:没有明显的事务中心,为变换型
- 找出变换中心:确定数据流边界
- 完成一级分解
- 完成二级分解
- 对初步软件结构精化
事务分析
信息流有明显事务特点(事务中心),采用事务分析方法
软件结构:一接收分支和一发送分支
人机界面设计
设计问题
系统响应时间
从用户完成某控制动作,到软件给出预期相应
两个重要属性:长度和易变性
关于长度:过长用户感到不安、沮丧
用户觉得系统立即相应时间范围0.1~1秒,超出1秒让用户注意到延迟
时间 | 显示 |
---|---|
1~10秒 | 鼠标显示称为沙漏 |
10~18秒 | 由微帮助来显示处理进度 |
18秒以上 | 显示处理窗口,或显示进度条 |
过短迫使用户加快操作节奏,导致出错
关于易变性:易变性指响应时间相对平均响应时间偏差,越低越好,否则会让用户误认为系统工作异常
用户帮助措施
手册和联机帮助(不离开用户界面)
联机帮助分为两类:集成帮助和附加帮助
继承帮助设计在软件里面,附加帮助系统建成后加到软件中,前者可用性更强
- 请求帮助:帮助菜单,特殊功能键,HELP命令
- 显示帮助信息:独立窗口、参考某个文档、屏幕固定位置做简短提示
- 组织帮助信息:
- 平面结构,通过关键字访问
- 层次结构,查更详细信息
- 超文本结构
出错信息处理
以用户可用理解术语
提供清楚、易理解报错信息(出错位置、原因)
从错误中恢复的建设性意见
信息用颜色等在视觉上引人注目
可能造成负面后果
命令交互
建议保留命令交互方式:
- 控制序列:Ctrl-C(拷贝)、Ctrl-H(帮助)、Ctrl-P(打印)
- 功能键:F1(帮助)
- 键入命令
- 命令宏机制:用户定义名字代表一个常用命令序列
设计指南
一般交互
保留人机界面菜单选择、命令输入、数据显示风格一致
提供有意义信息反馈:双向通信
破坏性动作前要确认:删除、覆盖
允许取消大多数操作
减少两次操作之间必须的记忆量
提高对话、移动和思考的效率
允许犯错误:保护不受致命错误破坏
按功能的动作分类,设计屏幕布局
提供帮助措施
用简单的动作或动词短语作为命令名
信息显示
显示与当前工作有关的信息
简单易懂方式表示数据:图形、图表
使用一致标记、标准缩写和可预知颜色
产生有意义出错信息
使用模拟的方式显示信息
数据输入
减少用户的输入动作:鼠标选择、滑动标尺等
使当前不适用命令不起作用
交互灵活:保留各种输入方式
让用户控制交流
对所有输入都提供帮助
消除冗余输入:数据单位、整数后键入.00、提供缺省值等
过程设计
过程设计任务
- 确定模块的算法
- 确定模块使用数据结构
- 确定模块接口(系统外部接口、用户界面、内部模块接口细节、输入数据和输出数据)
结构化程序设计
- 经典定义:程序代码通过顺序、选择、循环三种控制结构连接,单入口单出口
- 扩展定义:可限制使用GOTO语句、DO_UNTIL(先执行后判断,类似do…while)和DO_CASE(多分支,类似switch)
- 修正定义:LEAVE和BREAK,可从循环中转移出来
结构化程序设计工具
程序流程图:历史最悠久、使用最广泛的过程设计工具
- 顺序型:几个连续的加工依次排序
- 选择型:由某个判定的取值决定选择两个加工中的一个
- 当型循环型:当循环的控制条件成立时,重复执行特定的加工
- 直到型循环型:重复执行特定的加工,直到循环控制条件成立时
- 多情况选择型:列出多种加工情况根据控制变量的取值,选择执行其一
优点:对控制流程描绘直观,便于初学者掌握
缺点:
- 不是逐步求精好工具,过载考虑控制流程,非整体结构
- 用箭头代表控制流,程序员随意转移控制
- 不易表示数据结构和调用关系
N-S图(盒图):
特点:
- 功能域(一特定控制结构的作用域)明确
- 不可能任意转移控制
- 容易确定局部和全程数据的作用域
- 容易表现嵌套关系,也可表示模块的层次结构
PAD图:二维树形结构图来表示程序的控制流程
优点:
- 使用PAD图设计的程序必然是结构化程序
- PAD图描绘的程序结构十分清晰
- 用PAD图表现程序逻辑,易读、易懂、易记
- 容易将PAD图转换成高级语言源程序
- 支持自顶向下逐步求精
判定表:能清晰表示复杂的条件组合与应做动作间对应关系
左上部列出所有条件
左下部所有可能做的动作
右上部表示各种条件组合的矩阵
右下部是和每种条件组合相对应的动作
判定树:判定表变种,表示复杂条件组合与对应动作间对应关系
优点:形式简单,易看出含义,易于掌握和使用
缺点:简洁性不如判定表,相同数据元素重复写多遍,越接近叶端重复次数越多
过程设计语言:伪码,用正文形式表示数据和处理过程设计工具
PDL具有严格关键字外部语法,定义控制结构和数据结构
PDL表示实际操作和条件的内部语法灵活自由,适应各种工程师项目需要
程序复杂度
介绍使用比较广泛的McCabe方法
根据过程设计结果画出相应流图
流图描述程序控制流
计算流图的环形复杂度
V(G) = 区域数
V(G) = E - N + 2
E为流图中边数,N为流图中节点数
V(G) = P + 1
P为判定点数
面向数据结构设计方法
数据结构即影响程序的结构也影响程序的处理过程,可从数据结构导出程序的处理过程,适合详细设计
常见的面向数据结构的方法两种:Jackson和Warnier方法
Jackson图
描述数据结构:顺序、选择、重复
改进:直线,选择和循环结束条件
Jackson方法步骤
- 确定输入数据和输出数据逻辑结构,用Jackson图表达
- 确定输入结构和输出结构有对应关系(因果)的单元
- 描绘数据结构的Jackson图导出描绘程序结构的Jackson图
- 列出所有操作和条件,分配到Jackson图中
- 用伪码表示
实现
编码
编码目的:把模块的过程性描述翻译为用选定的程序设计语言书写的源程序(源代码)
依据:编码的主要依据是概要设计和详细设计说明文档
任务:理解概要设计和详细设计说明书;遵循编码原则和风格进行翻译,形成源代码
程序设计语言分类:
机器语言
1011011000000000:加法
1011010100000000:减法
优点:计算机直接识别
缺点:效率低,重用性差
汇编语言
机器指令助记符
优点:
- 比机器语言易读好、易调试和修改
- 执行速度快、占内存少
- 针对硬件编制
缺点:
- 不能编写复杂程序
- 依赖于机型、不通用、不可抑止
高级语言
与自然语言相近,面向用户的语言
优点:
- 编码效率高
- 通用性强,兼容性好,便于移植
缺点:
- 运行效率低
- 对硬件操作不如汇编语言
TIOBE反应编程语言的热门程度
- 语言的选择标准
- 系统用户要求:如果开发系统由用户维护,通常要求用熟悉语言编写
- 可以使用的编译程序:运行目标系统环境可提供编译程序限制可选用语言的范围
- 可以得到的软件工具:有支持程序开发的软件工具可以利用
- 工程规模:规模庞大,现有语言不适用,设计实现供该工程项目使用该程序设计语言
- 程序员知识:如果和其他标准不矛盾,应选择程序员熟悉的语言
- 软件可移植性要求:若目标系统在不同计算机上运行,选择可移植性好的语言
- 软件的应用领域:选择语言时应充分考虑目标系统的应用范围
编码风格:逻辑简明清晰、易读易懂是重要标准
可遵循一下五个方面:
- 程序内部的文档
- 数据说明
- 语句构造(简单)
- 输入输出
- 效率(和存储容量)
软件测试基础
软件测试的目标:
- 测试是为了发现程序中的错误而执行程序的过程
- 好的测试方案是极有可能发现迄今尚未发现的尽可能多的错误的测试
- 成功的测试是发现了迄今尚未发现的错误的测试
黑盒测试和白盒测试:
- 黑盒测试:如果知道产品应具有功能,可通过测试来检验是否每个功能都能正常使用
- 白盒测试:如果知道产品内部工作过程可通过测试来检验产品内部动作是否按照规格说明书的规定正常进行
测试准则:
- 所有测试应能追溯到用户需求,测试的目的是发现错误,其中最严重的是不能满足用户需求的错误
- 应尽早地和不断地进行软件测试
不应把软件测试仅看做是软件开发一独立阶段,应把它贯穿到软件开发各阶段中
- 充分注意测试中群集现象(Pareto原理)
测试后程序中残存错误数与程序中已发现错误数目成正比,80%错误与20%模块有关
- 测试应从小规模开始,逐步进行大规模测试
单个模块,逐步集成
- 不能做到穷举测试
穷举测试:程序所有可能执行路径都检查遍
- 第三方测试原则
从心理学角度考虑
逻辑覆盖(白盒测试)
测试数据在程序执行过程中覆盖路径
根据覆盖语句的详细程度,将覆盖分为不同等级:
- 语句覆盖:选择测试数据,使被测程序中每个语句至少执行一次
- 判定覆盖:每个语句至少执行一次,每个判定的真假分支至少执行一次
- 条件覆盖:每个语句至少执行一次,判定表达式每个条件取各种可能结果
- 判定/条件覆盖:取足够多测试数据,使判定表达式每个条件都取到各种可能值,且每个判定表达式也都能取到各种可能结果
- 条件组合覆盖:选足够多的数据,使每个判定表达式中条件的各种组合都至少执行一次
控制结构测试(白盒测试)
基本路径测试
Tom McCabe提出的一种白盒测试技术
- 根据过程设计结果画出相应流图
- 计算流图的环形复杂度
V(G) = e - n + 2
e为边数,n为节点数
V(G) = 图所在平面被划分为的区域数
V(G) = 判定节点数 - 1
确定线性独立路径的基本集合
独立路径:至少包含一条在定义该路径之前不曾用过的边
环形复杂度为独立路径基本集的上界
设计测试用例覆盖基本测试集合
注意:一些独立路径无法独立测试,程序的正常流程不能形成独立执行该路径所需的数据组合,这种情况下这些路径必须作为其他路径测试的一部分
循环测试
- 简单循环
- 零次循环:从循环入口直接跳到循环出口
- 一次循环:查找循环初始值方面的错误
- 二次循环:检查在多次循环时才能暴露的错误
- m次循环:此时的m<n
- 最大次数循环、比最大次数循环多一次的循环、比最大次数循环少一次的循环
- 嵌套循环
- 从最内层循环开始,置所有其他层循环为最小值
- 对最内层循环做简单循环的全部测试
- 逐步外推,测试时保持所有外层循环变量取最小值,其他嵌套内层循环变量取“典型”值
- 反复进行,知道所有各层循环测试完毕
- 连锁循环
- 各个循环互相独立,可用于简单循环相同方式进行测试
- 几个循环不是互相独立,需要使用测试嵌套循环
- 非结构化循环
- 使用结构化程序设计方法重新设计
黑盒测试
黑盒测试重测:软件功能
黑盒发现错误类型:
- 功能不正确或遗漏
- 界面错误
- 数据结构或外部数据库访问错误
- 性能错误
- 初始化或终止错误
常见的黑盒测试技术:
等价类划分
把程序的输入域划分成若干数据类,从每一数据类选取少数有代表性数据作为测试用例
在各数据类中,各输入数据对揭露程序中的错误等效
- 有效等价类:合理,有意义输入数据结构集合
- 无效等价类:不合理,无意义输入数据构成的集合
等价类划分原则:
- 输入条件规定范围,定义一有效等价类和两无效等价类
- 输入条件是布尔量,一个有效等价类和一个无效等价类
- 规定输入数据一组值,程序对每个输入值分别进行处理。每个输入值确立一有效等价类,针对这组值确立一个无效等价类
- 规定输入数据必须遵守规则,定义一有效等价类(符合规则)和若干无效等价类(从不同角度违反规则)
- 已划分等价类中各元素在程序中处理方式不同,将等价类进一步划分为更小等价类
边界值分析
等价类划分补充,确定边界情况。选择正好等于边界值做测试数据;选临近边界合法数据;选刚超边界值非法数据
边界值选择原则:
- 输入条件规定了取值范围,则以该范围作为边界
- 输入条件规定值的个数,则以个数为边界
- 针对规格说明的每个输入条件,使用以上两个
- 如果规格说明给出的输入或输出域是有序集合,则选取集合中特定次序的元素作为边界
- 如果程序中使用一个内部数据结构,则应选择该结构的边界上的值
- 分析规格说明,找出其他可能边界条件
错误推测法
靠经验和直觉推测程序可能存在错误,有针对编写检查这些错误的测试用例
测试策略
测试步骤
单元测试
模块通过编译的语法检查后进入单元测试
测试内容:
- 模块接口:数据是否正确进出模块
- 局部数据结构:局部数据的说明、初始化、默认值是否有问题
- 边界条件:软件容易在边界上失效
- 重要的执行路径:重要执行路径是否计算错误、不正确比较或不适当控制流
- 错误处理:
- 错误描述是否难于理解
- 记下错误是否与实际遇到错误不同
- 错误处理之前,错误条件已引起系统干预
- 错误处理不正确
- 描述错误信息不足以帮助确定错误位置
测试方法:
- 代码审查(人工):先由编写人非正式进行,再由审查小组正式进行,可查30%到70%设计错误和编码错误
- 需要辅助模块(驱动模块、桩模块)与被测模块相连
- 驱动模块,相当于被测模块主程序。接收测试数据,传送给被测模块,在输出测试结果
- 桩模块,存根模块。代替被测模块调用的子模块
集成测试
测试与接口有关问题:
- 穿越接口数据是否丢失
- 一模块功能是否对另一模块功能产生不利影响
- 各子功能组合起来,能否达到预期的父功能
- 全局数据结构是否有问题
- 单个模块误差累计起来,是否会放大
测试方法:
- 非渐增式集成:把所有模块一次组装进行测试
- 渐增式集成:
- 自顶向下集成
- 自底向上集成
- 混合策略
- 改进的自顶向下测试方法:基本用自顶向下方法,早期用自顶向上测试关键模块
- 混合法:软件结构上层模块用自顶向下,下层用自底向上
回归测试(集成测试中包含)
重新执行已作过测试的某子集,保证变化没带来非预期副作用
回归测试集:
- 检测软件全部功能的代表性测试用例
- 专门针对可能受修改影响的软件功能附加测试
- 针对被修改过软件功能测试
系统测试
使软件和其他系统元素(硬件、数据库等)结合测试
测试方法:
恢复测试:以不同方式强制软件出现故障,检测软件能否恰当完成恢复
- 自动恢复:检测重新初始化、数据恢复、重新启动等是否正确
- 人工干预恢复:检测平均恢复时间是否在允许范围内
安全性测试:突破软件安全保护机构的安全保密措施,检验系统预防机制的漏洞
- 测试者扮演试图攻击系统角色
- 通过外部手段获取密码
- 通过客户软件攻击系统
- 控制系统使其他人无法访问
- 引发系统错误,期望在系统恢复中入侵系统等
强度测试:检测系统能力最高达到时间限度,让系统出于资源异常数量、异常频率、异常批量条件下测试系统承受能力。
一般比平常限度高5-10倍的限度做测试用例
性能测试:软件运行性能与性能要求比较,检验是否达到性能要求规格
确认测试(验收测试)
系统测试后,客户再验收测试
确认测试以需求规格说明书为测试基础,采用黑盒测试法
α测试:用户对即将面试软件产品(称α版本)进行测试,开发者坐在用户旁边,随时记下错误情况和使用中问题,是受控环境下测试
目的:评价软件功能、可使用性、可靠性、性能、界面
β测试:多个用户在实际使用环境下进行的测试。用户与公司签定支持产品预发行合同,使用产品并返回错误信息。是在开发者无法控制的环境下进行的软件现场应用
目的:产品的支持性
调试
软件调试是在进行了成功的测试之后才开始的工作。它与软件测试不同,调试的任务是进一步诊断和改正程序中潜在的错误
调试活动:
- 确定程序错误的性质和位置
- 修改程序,排除错误
- 从错误现场入手,确定程序出错位置
- 找错误的内在原因
- 找到则排除错误,回归测试
- 否则,加测试用例证明猜测原因
调试方法:
强行排错:
- 将内存内容打印出来
- 程序特定部位设置打印语句
各关键变量改变位置、重要分支部位等,监视重要变量变化
- 自动调试工具
程序语言调试功能或专门交互式调试工具,不必修改程序。如设置断点,观察程序断点处状态,包括变量、表达式值等
回溯法排错(小程序常用):
确定最先发现“症状”位置,人工沿程序控制流程向回追踪源代码,直到找到错误根源或确定错误范围
原因排除法:
对分查找法
- 如已知每个变量在程序中若干关键点正确值,用赋值语句或输入语句在程序中点附近“注入”正确值,运行程序检查输出
- 正确,错误原因在注入语句前;反之,在注入语句后
- 反复使用缩小范围
归纳法调试:
- 归纳法是一种从特殊推断一般的逻辑方法
- 归纳法调试的想法是:从一些线索(错误征兆)着手,通过分析它们之间的关系找出错误
演绎法排错:演绎法是从一般原理或前提出发,经过排除和精化的过程来推导出结论的逻辑方法
- 根据已有测试用例,设想所有可能出错原因
- 逐个排除不正确的
- 验证余下假设确是出错原因
软件的可靠性
基本概念:
- 可靠性:程序在给定时间间隔及环境条件下,按规格说明书的规定成功运行的概率
- 可用性:给定的时间点,按规格说明书规定,成功运行概率
估算平均无故障时间:
符号:
- E
T:测试前程序中错误总数 - I
T:程序长度(机器指令总数) - τ:测试时间(包括调试)
- E
d(τ):0到τ间发现的错误数 - E
c(τ):0到τ间改正的错误数
- E
基本假定:
- 单位长度的错误数E
T/IT,近似常数 - MTTF与剩余错误数成反比
- 为简化讨论,假设E
d(τ)=Ec(τ)
- 单位长度的错误数E
估计错误总数方法
植入错误法
测试之前由专人在程序中随机植入错误,测试后根据发现错误中原有的和植入的两种错误比例,估计程序中错误总数
分别测试法
如果能对程序中原有的错误加上标记,然后在通过进一步的测试,就可以对潜在故障进行估计
面向对象方法学引论
面向对象方法学概述
传统软件工程方法学适用于中小型软件产品开发,面向对象软件工程方法学适用于大型软件产品开发
面向对象方法学方程式:
00 = 对象 + 类 + 继承 + 传统消息实现通信
面向对象方法学概念
对象:具有相同状态的一组操作的集合,对状态和操作的封装
类:对具有相同状态和相同操作的一组相似对象的定义
实例:实例是由某个特定类所描述的一个具体对象
消息:要求某对象执行某个操作的规格说明
- 三部分组成:接收消息的对象、消息名、0或多个变元
方法和属性
- 方法:对象执行的操作,即类中定义的服务
- 属性:类中所定义数据,对客观世界实体具体性质的抽象
继承:子类自动共享基类中定义的属性和方法的机制
多态性:在类等级不同层次可共享一个方法名,不同层次每个类按各自需求实现这个方法
多态的优点:
- 提高程序可复用性(接口设计的复用,不是代码实现复用)
- 派生类的功能可被基类指针应用,提高程序可扩充性和可维护性
重载:
- 函数重载:在同一作用域内,参数特征不同的函数可使用相同的名字
- 调用者不需记住功能雷同函数名,方便用户
- 程序易于阅读和理解
- 运算符重载:同一运算符可施加于不同类型操作数上面
与传统方法学比较
传统方法:
- 系统是过程的集合
- 过程与数据实体交互
- 过程接收输入并产生输出
面向对象方法:
- 系统使交互对象的集合
- 对象于人或其他对象交互
- 对象发送与响应消息
面向对象方法的优点
- 与人类习惯思维方法一致:对象是对现实世界正确抽象,问题空间和解空间结构一致
- 稳定性好:软件系统结构根据问题领域模型建立,功能需求变化不会引起软件结构整体变化,做局部性修改
- 可重用性好
UML
UML全程为Unified Modeling Language目前最流行的面向对象建模语言
建模必要性
捕获商业流程
促进沟通
管理复杂性
定义软件架构
促进软件复用性
UML发展
UML是图形化、说明、构造一个软件系统并生成其文档的标准语言
UML独立于开发过程,可与大多数面向对象开发过程配合使用
UML独立于程序设计语言,可用C++、Java等任何一种面向对象程序设计语言实现
UML构成
基本构造块:事务、关系、图
规则:名字、作用域、可见性、完整性、执行
公共机制:规范说明、修饰、公共划分、扩展机制
UML视图
不同的视图突出特定的参与群体所关心的系统的不同方面,通过合并所有五个视图中得到的信息皆可以形成系统的完整描述
用例视图:定义了系统的外部行为,是最终用户、分析人员和測试人员所关心。该视图定义了系统的需求,因此约束了描述系统设计和构造的某些方面的所有其他视图
设计视图:描述的是支持用例视图中规定的功能需求的逻辑结构。它由程序组件的定义,主要是类、类所包含的数据、类的行为以及类之间交互的说明组成
实现视图:描述构造系统的物理组件,这些组件包括如可执行文件、代码库和数据库等内容。这个视图中包含的信息与配置管理和系统集成这类活动有关
进程视图:进程视图包括形成井发和同步机制的进程和线程
部署视图:部署视图描述物理组件如何在系统运行的实际环境(如计算机网路)中分布
UML静态建模-用例图
用例图描述外部执行者(actor)与系统的交互,表达系统功能,即系统提供服务
主要元素:用例和执行者
用例:执行者与计算机一次典型交互,代表系统某一完整功能
执行者:描述与系统交互的人或物,代表外部实体(如用户、硬件设备或其他软件系统)
建立用例模型:
- 发现执行者
- 获取用例
- 执行者间关联:泛化关系
- 用例间关联:泛化关系、扩展关系、包含关系
UML静态建模-类图、对象图
类图是面向对象建模最常用的图,描述类与类间的静态关系
类图主要有三部分组成:类名、属性、操作
类属性的语法:[][][][][可见性] 属性名 [:类型] [=初值]
可见性:公有(+)、私有(-)、保护(#)
类操作的语法:[可见性] 操作名 [(参数列表)] [:返回类型]
边界类:位于系统与外界的交界处
- User interface boundary class
- External system boundary class
控制类:每个用例通常都有一个控制类,控制用例中的事件顺序,控制类也可以在多个用例间共用
实体类:用于对必须存储的信息和相关行为建模的类
接口类:描述一个类或构件服务的操作集,不含属性,只包含方法和声明
类之间的关系:
关联:普通关联、导航关联、限定关联
关联类:用关联类记录关联的附加信息
整体部分关系:聚合(整体部分较弱)、组合(整体部分较强)
泛化(继承):类间的一般特殊关系
依赖:一个模型元素变化会影响另一个模型元素
实现:一个类描述了另一个类保证实现的合约
对象图表示一组对象之间联系,对象图是类图的实例
包是对模型元素成组的通用机制
包的联系:依赖、泛化
UML静态建模-顺序图、协作图、活动图
对象间交互通过消息
- 简单消息:没有描述通讯的细节
- 同步消息:调用者发出消息之后要等待消息返回才能继续执行
- 异步消息:调用者发出消息之后不等待消息返回才能继续执行
- 返回消息:代表从过程调用的返回
顺序图描述对象间交互关系
协作图描述相互协作对象间交互关系和链接关系
活动图描述为完成某一用例需要做的活动以及这些活动的执行顺序
UML物理框架机制
系统架构:逻辑架构、物理架构
逻辑架构:描述系统功能
物理架构:关心的是实现,构件图和配置图
构件图展示一组构件的类型、内部结构和它们之间的依赖关系
构件代表系统一物理实现块,一般作为一独立文件存在
构件种类:
- 部署构件:是构成一可执行系统必要构件
- 工作产品构件:开发工程产物,包括源代码文件及数据文件。构件不直接参与可执行系统,用来产生可执行系统的中间产品
- 执行构件:构成一可执行系统必要构件
配置图描述了系统硬件和软件物理配置情况和系统体系结构,显示系统运行时刻的结构
配置图包含结点和连接两个元素,配置图中的结点代表实际的物理设备以及在该设备上运行的构件和对象
配置图各结点之间进行交互的通信路径称为连接
UML的扩展机制
利用扩展机制,用户可定义使用自己的模型元素
标签值是存储元素相关信息字符串,可附加在任何独立元素(图形元素、视图元素)
标签是建模人员需要记录某些特性的名称
值是给定特性的值
约束是用文字表达式表达的语义限制,对声明全局的或影响大量元素的条件特别使用
约束表示为括号中的表达式字符串,附加在类、对象、关系上和注释上等
版类在模型本身中定义的一种模型元素,UML元素具有通用语义,利用版类进行转优化和扩展,在已有元素上增加新语义
版类用纺织在基本模型元素符号中或附近的被《》括起来的文字串显示,还可为特殊版型创建图标,替换基本元素符号
面向对象分析
面向对象分析过程
获取需求
- 与用户交谈,向用户提问题
- 参观用户的工作流程,观察用户的操作
- 向用户群体发调查问卷
- 与同行、专家交谈,听取他们的意见
- 分析已经存在的同类软件产品,提取需求
- 从行业标准、规则中提取需
- 从 Internet 上搜查相关资料等
整理需求
书写需求陈述:去求陈述内容包括问题范围、功能需求、性能需求、应用环境及假设条件
建立模型
建立三个独立模型:
- 功能模型:指明系统应“做什么”,由用例图表示
- 对象模型:描述静态结构,定义做事情实体,类图和对象图表示
- 动态模型:描述交互过程,由状态图和顺序图表示
书写需求规格说明书
复审
建立功能模型
功能模型用用例图表达,研究需求陈述建立用例图
步骤:
- 识别执行者
- 识别用例
- 建立用例图
- 补充用例描述:为建立对象模型和动态模型打基础
建立对象模型
对象模型描述类及相互关系,表达目标系统静态结构
建立对象模型步骤:
确定分析类
边界类、控制类、实体类
分析模型中,分析类是概念层次上的内容,类直接与应用逻辑相关,不关注技术实现
一执行者与一用例间交互或通信关联对应一边界类
控制类负责协调边界类和实体类,通常在现实世界没有对应的事物
实体类通常是用例图中的参与对象,对应着现实世界中“事物”
确定类的关联
初步确定关联、筛选、进一步完善
划分主题
确定属性
需求陈述中的名词
识别继承
反复修改
建立动态模型
开发交互式系统,动态模型非常重要
建立动态模型步骤:
- 编写典型交互行为脚本
- 从脚本中提取事件及相关对象,用顺序图表达
- 确定对象状态及状态间转换关系,用状态图描述
编写脚本:脚本描述用户与目标系统间的一个或多个典型交互过程
画顺序图:从脚本提取所有外部事件,确定每类事件发送和接收对象
画状态图:用一张状态图描绘类的行为,集中考虑具有交互行为类
面向对象设计
面向对象设计准则及启发规则
分析:提取、整理用户需求,建立问题域精确模型
设计:转变需求为系统实现方案,建立求解域模型
- 在实际的软件开发过程中分析和设计的界限是模糊的
- 分析和设计活动是一个多次反复迭代的过程
- 面向对象方法学在概念和表示方法上的一致性,保证了在各项开发活动之间的平滑(无缝)过渡,领域专家和开发人员能够比较容易地跟踪整个系统开发过程,这是面向对象方法与传统方法比较起来所具有的一大优势
抽象:通过像类抽象机制实现,提高可重用性
信息隐蔽:通过封装性实现,提高独立性
弱耦合:交互耦合(松散)、继承耦合(紧密)
强内聚:服务内聚、类内聚、一般特殊内聚
可重性:尽量利用已有类,创建新类考虑以后可重用性
设计问题域子系统
设计基础:分析阶段精准问题域模型
设计任务:从实现角度补充、修改问题域模型
调整需求
用户需求或外部环境变化、分析模型不完整准确
重用已有类
根据问题解决的需要,把从库或其他来源得到既存类增加到问题解决方案中
把问题域类组合在一起
设计时,从类库中引进一个根类,作为包容类,把所有与问题域有关的类关联到一起,建立类的层次
增加一般化类
某些特殊类要求一组类似的服务,应加入一般化的类,定义为所有特殊类共用的一组服务名,服务都是虚函数
调整继承关系
设计人机交互子系统
分析阶段:用户界面需求
设计阶段:确定人机交互细节,窗口报表形式,命令层等
Theo Mandel创造了三条黄金原则:
- 置用户于控制之下
- 减少用户的记忆负担
- 保持界面一致
允许用户操作控制的原则:
- 交互模型的定义不能强迫进入不必要的或不希望的动作的方式
- 提供灵活的交互
- 允许用户交互可以被中断和撤销
- 当技能级别增长时可以使交互流水化并允许定制交互
- 使用户隔离内部技术细节
能够减少用户记忆负担:
- 减少对短期记忆的要求
- 建立有意义的缺省
- 定义直觉性的捷径
- 界面的视觉布局应该基于真实世界的隐喻
- 以不断进展的方式揭示信息
用户应以一致的方式展示和获取信息:
- 所有可是信息的组织均按照贯穿所有屏幕显示所保持的设计标准
- 输入机制被约束到有限的集合,在整个应用中被一致地使用
- 从任务到任务的导航机制被一致地定义和实现
设计任务管理子系统
在实际系统中,许多对象之间往往存在相互依赖关系
设计工作的一项重要内容就是,确定哪些是必须同时动作的对象,哪些是相互排斥的对象。进一步设计任务管理子系统
系统总有许多并发行为,需按照各自行为的协调和通信关系,划分各种任务(进程),简化并发行为的设计和编码
确定各类任务,把任务分配给适当的硬件和软件去执行
根据动态模型分析、定义并发行。
分析并发性:
并发对象:无交互行为的对象、同时接收事件的对象
定义任务:检查各个对象的状态图,找到并发对象的路径(任何时候路径中只有单个对象是活跃的),称控制线
通过分离出控制线设计任务
设计任务子系统:
事件驱动型
指睡眠任务(不占用 cpu),某个事件发生,任务被触发,醒来做相应处理,又回到睡眠状态
时钟驱动型任务
按特定时间间隔去触发任务进行处理
确定优先任务
高优先级,分离成独立任务,保证时间约束
确定关键任务
严格可靠性,分离考虑,精心设计和编码,严格测试
确定协调任务
三个以上任务,引入协调任务,控制封装任务间协调
尽量减少任务数
任务多,设计复杂、不易理解、难维护
确定资源需求
计算系统载荷,每秒处理业务数,处理一个业务花费时间,估算所需 cpu (或其他固件)处理能力
综合考虑,确定哪些任务硬件实现,哪些任务软件实现
注:任务管理部件一般在信息系统中使用较少;在控制系统中应用较多
设计数据管理子系统
选择数据管理模式:
文件管理系统:
- 成本低,简单
- 操作级别低,不同操作系统的文件系统差别大
关系数据库管理系统
面向对象数据库管理系统
扩展的关系数据库管理系统:増加抽象数据类型,继承等机制,如oracle8.0
扩展的面向对象语言:増加数据库存储和管理对象机制
设计数据管理子系统
设计数据格式,与数据存储管理模式密切相关:
(1)文件系统:达到第一范式;减少文件数;编码减少文件中属性值
(2)关系数据库管理系统:达到第三范式,满足性能和存储需求
(3)面向对象数据库管理系统:同(2)
程序设计风格
####面向对象程序设计风格
面向对象的是实现:
- 把面向对象设计结果翻译成面向对象程序
- 测试并调试面向对象的程序
程序设计语言:所有语言都可完成面向对象实现,但效果不同
- 使用非面向对象语言编写面向对象程序,则不许有程序员自己把面向对象概念映射到目标程序中
- 选用面向对象语言优点
面向对象的准则:
- 提高可重用性:提高方法的内聚,减小方法的规模,保持方法的一致性,策略与实现分开,全面覆盖,尽量不用写全局信息,利用继承机制
- 提高可扩充性:封装实现层略,不用一个方法遍历多条关联链,避免多分支语句,精心确定共有方法
- 提高健壮性:预防用户操作错误,检查参数合法性,不预先确定限制条件,先测试后优化
测试策略和测试用例设计
单元测试
单元:封装的类和对象
对程序内部具体单一模块进行测试
传统的测试方法都可以使用,等价类划分、边值分析、逻辑覆盖法、基本路径法
集成测试
在面向对象的软件中不存在层次的控制结构,传统的自顶向下或自顶向上的集成策略就没意义了
由于构成类的各个成分彼此间存在直接或间接的交互,一次集成一个操作到类中(传统的渐增式集成方法)通常是不现实的
面向对象软件的集成测试主要有下述两种不同的测试
- 基于线程的集成测试
- 基于使用的集成测试
确认测试
测试用户可见动作,可识别系统输出
根据动态模型的描述系统行为的脚本设计确认测试用例(黑盒)
测试用例设计:测试类的方法、集成测试方法
软件项目管理
软件项目管理:通过计划、组织、控制一系列活动,合理配合使用资源,达到既定目标的活动
软件规模度量:代码行技术、功能点技术
工作量估算:静态变量模型、动态多变量
进度计划:Walston_Felix模型、原始的COCOMO模型、COCOMO2模型、Putnam模型,Gantt图(横道图)
软件开发组织形式:民主制小组、主程序员制小组、现代程序员组
风险控制:项目风险、技术风险、商业风险
质量控制:McCall模型
配置管理:Rational ClearCase、Microsoft SourceSafe