软件构造复习1

目录
软件构造:开发高质量的软件系统
软件构造的多维度视图和质量目标
软件测试与测试优先的编程
软件构造过程与配置管理
数据类型与类型检验
设计规约
抽象数据类型(ADT)
面向对象的编程(OOP)
ADT和OOP中的“等价性”
面向复用的软件构造技术
面向可维护性的构造技术
面向可复用性和可维护性的设计模式
面向正确性与健壮性的软件构造

0.软件构造:开发高质量的软件系统
设计目标:编程的“视野”
设计原则:编程的“标尺”
设计模式:编程的“经验”

1.软件构造的多维度视图和质量目标
1.软件构造的多维度视图
按阶段划分:构造时/运行时视图
按动态性划分:时刻/阶段(形态)视图
按构造对象的层次划分:代码(逻辑)/构件(物理)视图

2.阶段划分、各阶段活动


3.软件质量指标
(1)外部指标(影响用户)
①正确性
按照预先定义的“规约”执行,至高无上的质量指标。
需要在软件系统的每一层保证自己的正确性,同时假设其下层是正确的。
通过测试和调试、防御式编程、形式化方法等手段保证正确性
②健壮性
针对异常情况(取决于spec的范畴)的处理,出现异常时不要“崩溃”。
出现规约定义之外的情形的时候,软件要做出恰当的反应,是对正确性的补充。
未被specification覆盖的情况即为“异常情况”
③可扩展性
对软件的规约进行修改,是否足够容易?
两种原则:简约主义设计,分离主义设计
④可复用性
发现共性,一次开发,多 次使用
不要不断重复自己写过的东西,不要重复造轮子
⑤兼容性
不同的软件系统之间相互可容易的集成
需要保证设计的同构性:
-标准统一的文件格式
-标准统一的数据结构
-标准统一的用户接口
-标准统一的访问协议
⑥性能
性能是软件系统对硬件资源尽可能少地提出要求的能力,例如处理器时间、内部和外部存储器占用的空间、通信设备中使用的带宽。
性能毫无意义,除非有足够的正确性
对性能的关注要与其他质量属性进行折中
过度的优化导致软件不再适应变化和复用
⑦可移植性
软件可方便的在不同的技术环境(硬件、操作系统)之间移植’
⑧易用性
容易学、安装、操作、监控
给用户提供详细的指南
结构简单,了解用户。
⑨功能性
功能性是系统提供的可能性的程度。
程序设计中一种不适宜的趋 势,即软件开发者增加越来越多的功能,企图跟上竞争,其结果是程 序极为复杂、不灵活、占用过多的磁盘空间
每增加一小点功能,都确保其他质量属性 不受到损失
⑩及时性
软件系统在用户需要时或之前发布的能力。
(2)内部指标(影响软件本身和它的开发者,外部质量取决于内部质量)
源代码相关因素,如代码行数(loc)、循环复杂性等
体系结构相关因素,如耦合、内聚等
可读性
可理解性
清晰度
大小
(3)指标折中
正确的软件开发过程中,开发者应该将不同质量因素之间如何做出折中的设计决策和标准明确的写下来
虽然需要折中,但“正确性”绝不能与其他质量因素折中。
最重要的几个质量因素:正确性、健壮性(可靠),复用性、可扩展性(模块化)

2.软件测试与测试优先的编程
§ 认可“测试”的价值,搞清楚“测试优先”的哲理
§ 学会用等价划分和边界值分析方法为模块设计测试用例
§ 可用工具度量一组测试用例对代码的“覆盖度”
§ 各种各样的测试,都有些初步了解
大纲:
(1)软件测试
(2)测试用例
(3)测试优先的编程/测试驱动开发(TDD)
(4)单元测试
(5)使用JUnit进行自动化单元测试
(6)黑盒测试
——等价类划分
——边界值分析
(7)白盒测试
(8)覆盖度
(9)集成测试
(10)回归测试
(11)在程序中文档化测试策略

黑盒测试用例的设计
– 等价类划分、边界值分析

以注释的形式撰写测试策略
JUnit测试用例写法
测试覆盖度
什么是测试?
(1)软件测试是提高软件质量的重要手段
发现bugs, 确认是否达到可用级别(用户需求)
关注系统的某一侧面的质量特性
(2)即使是最好的测试,也无法达到100%的无错误
(3)每 kloc(1000 行代码)的一些典型残留缺陷率(软件发布后遗留的错误):
——1-10 个缺陷/kloc:典型的行业软件。
——0.1-1 缺陷/kloc:高质量验证。 Java 库可能达到这种正确性水平。
——0.01-0.1 缺陷/kloc:最好的安全关键验证。NASA 和 Praxis 这样的公司可以达到这个水平。
(4)这对于大型系统来说可能令人沮丧。 例如,如果你已经发布了 100 万行典型的行业源代码(1缺陷/kloc),这意味着您错过了 1000 个错误!
(5)测试跟其他活动的目标相反:破坏、证错、“负能量”
(6)再好的测试也无法证明系统里不存在错误
(7)什么是好的测试:
——能发现错误
——不冗余
——最佳特性,多种测试方案中最好的
——别太复杂也别太简单

测试:发现是否存在错误
调试:识别错误根源,消除错误

==白盒测试:==对程序内部代码结构的测试
==黑盒测试:==对程序外部表现出来的行为的测试

什么是测试用例:
test case = {test inputs + execution conditions+ expected results}
测试用例:输入+执行条件+期望结果

好的测试用例的特点:
(1)最可能发现错误
(2)不重复、不冗余
(3)最有效
(4)既不简单也不复杂

测试优先的编程:
定义:在编写代码之前先编写测试。
为什么要进行测试优先的编程:
(1)尽早并经常进行测试,使程序快速出错。
(2)不要把测试留到最后,当有一堆未验证的代码,将测试留到最后只会使调试时间更长,因为错误可能在代码的任何地方。
(3)“测试代码”比写代码更有成就感
过程:
(1)先写spec
(2)再写符合spec的测试用例
(3)写代码、执行测试、有问题再改、再执行测试用例,直到通过

Unit Testing 单元测试:
定义:针对软件的最小单元模型开展测试,隔离各个模块,容易定位错误和调试
黑盒测试:
定义:用于检查代码的功能,不关心内部实现细节

等价类划分
边界值
白盒测试
代码覆盖度
回归测试

3.软件构造过程与配置管理
了解软件构建的一般流程(广义的软件构造过程):
设计—>编程—>调试—>测试—>构建—>发布

大纲:
▪ 软件开发生命周期(SDLC)
▪ 传统软件过程模型(瀑布、增量、V字、原型、螺旋)
▪ 敏捷开发和极限编程(XP)
▪ 软件配置管理(SCM)
▪ Git作为SCM工具
▪ 总结

软件开发生命周期(SDLC): From 0 to 1 从无到有


软件生命周期中的多个版本: From 1 to n 从有到好

传统软件过程模型:
Linear 线性过程

Iterative 迭代过程

敏捷开发和极限编程(XP):
敏捷开发:通过快速迭代和小规模的持续改进,以快速适应变化。

软件配置管理(SCM):
定义:追踪和控制软件的变化
核心:版本控制和基线的确立
软件配置项:软件中发生变化的基本单元(例如:文件)
基线:软件持续变化过程中的“稳定时刻”(例如:对外发布的版本)
版本:为软件的任一特定时刻(Moment)的形态指派一个唯一的编号,作为“身份标识”

Git作为SCM工具
Refactoring重构:在不改变功能的前提下优化代码

4.数据类型与类型检验
大纲:

编程语言中的数据类型
静态与动态数据类型检查
可变性和不变性
快照图
复杂数据类型:数组和集合
有用的不可变类型
总结

数据类型:一组值以及可以对其执行的操作
变量:用特定数据类型定义,可存储满足类型约束的值

5.设计规约
大纲:

编程语言中的函数/方法
规范:通讯编程
为什么需要规范
行为对等
规范结构:前置条件和后置条件
测试和验证规格
设计规范
分类规格
制图规格
规范的质量
总结
写在方法前面的说明性注释,用来解释方法的功能、参数、返回值等。如下图所示:

规约的好处
记录自己的设计决策,供自已或他人阅读。
团队协作中,如果没有规约就无法分派任务,无法写程序;即使写出来,也无法验证对错。
规约作为一种客户端与程序之间的“契约”,可以明确双方的责任。
规约可以隔离变化,无需通知客户端,即不管具体实现如何修改,只要符合规约,客户端就没必要知道修改的具体内容。
达到解耦的目标。
可以利用规约来判定方法的行为等价性。
测试用例可以根据规约来编写。

(方法的)规范和合同
规格(或称为合同)
▪ 规范是团队合作的关键。没有规范就不可能委派实现方法的责任。
▪ 规范充当一个契约,实现者负责满足契约,而使用该方法的客户机可以依赖该契约。
–说明方法和调用方的职责
–定义实现正确的含义
▪ 规范对双方都有要求:当规范有前提时,客户也有责任。

为什么需要规格?
▪ 现实:程序中许多最糟糕的错误都是由于对两段代码之间的接口行为的误解而产生的。
–虽然每个程序员都有自己的规范,但并不是所有的程序员都把它们写下来。因此,团队中的不同程序员有不同的规范。
–当程序失败时,很难确定错误在哪里。
▪ 优点:代码中的精确规范可以让您将责任分摊到代码片段上,并且可以让您免去为修复应该去哪里而费解的痛苦。
–规范对于方法的客户端来说很好,因为它们省去了读取代码的任务。

规范(合同)
▪ 该契约充当客户端和实现者之间的防火墙。
–它保护客户不受该单元工作细节的影响。
–它保护实现者不受单元使用细节的影响。
–此防火墙令客户端和程序不耦合,只要更改符合规范,允许独立更改单元代码和客户端代码。

行为等价性
为了确定行为等价性,问题是我们是否可以用一个实现代替另一个实现

规范结构:前置条件和后置条件
规格结构
▪ 方法的规范由几个子句组成:
–Precondition,由关键字requires指示
–Postcondition,由关键字effects指示
–Exceptional behavior:如果违反了Precondition,它会做什么
▪ 先决条件是客户的义务(即方法的调用方)。它是调用方法的状态的条件。
▪ 后置条件是方法实现者的义务。
▪ 如果调用状态的前提条件成立,则该方法有义务通过返回适当的值、抛出指定的异常、修改或不修改对象等方式遵守后条件。

规格结构
▪ 整体结构是一个逻辑含义:如果在调用方法时前置条件保满足了,则在方法完成时后置条件必须满足。▪ 如果在调用方法时前置条件不成立,则实现不受后置条件的约束。–它可以做任何事情,包括不终止、抛出异常、返回任意结果、进行任意修改等。

按照强弱分类
规约A强度>B规约强度意味着下面两点:

A的前置条件更弱
A的后置条件更强
前置条件和后置条件的强弱由条件的严格程度决定,即如果条件越放松,那么这个条件越弱。反之亦然。

如果要增强一个规约的强度,那么就意味着更松的前置条件+更严格的后置条件。


上图中第二个规约的前置条件比第一个规约的前置条件更加放松,因此规约变强。第三个规约的后置条件更加严格,所以强度更高。

但是要注意一点,后置条件的范围变大,并不意味着后置条件就变弱了,是否变弱需要看前置条件对应的那一部分后置条件是否变弱。如下图,规约二的前置条件变弱了,后置条件的范围变大,但是在满足规约一中的前置条件的情况下,此后置条件并没有变化,所以后置条件并没有变弱。因此整个规约二的强度变大了。

再看下面这个例子,在满足规约一的前置条件的情况下,规约二的后置条件相对于规约一的后置条件变弱了。但是由于前置条件也是变弱,因此无法比较这两个规约的强弱。

按照确定性分类
确定性规约(Deterministic):给定前置条件,其输出是唯一的、明确的。

欠定的规约(Underdetermined):同一个输入可以有多个输出,但是一旦使用具体的方法实现了这个规约,那么这个返回值也将会被确定。

非确定的规约(Not deterministic):同一个输入,多次执行可能得到多个结果,比如涉及到随机数的方法。

按照陈述性分类
操作式规约:有具体的实现细节,如伪代码

声明式规约:没有内部实现的描述,只有对输入输出的规定

声明式规约更有价值,更能应对变化。但是操作式规约能够方便开发。
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值