软件构造知识点理解与总结:
目录
- 软件构造:开发高质量的软件系统
- 软件构造的多维度视图和质量目标
- 软件测试与测试优先的编程
- 软件构造过程与配置管理
- 数据类型与类型检验
- 设计规约
- 抽象数据类型(ADT)
- 面向对象的编程(OOP)
- ADT和OOP中的“等价性”
- 面向复用的软件构造技术
- 面向可维护性的构造技术
- 面向可复用性和可维护性的设计模式
- 面向正确性与健壮性的软件构造
什么是测试用例:
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 从无到有
Iterative 迭代过程
数据类型:一组值以及可以对其执行的操作
变量:用特定数据类型定义,可存储满足类型约束的值
规约的好处
记录自己的设计决策,供自已或他人阅读。
团队协作中,如果没有规约就无法分派任务,无法写程序;即使写出来,也无法验证对错。
规约作为一种客户端与程序之间的“契约”,可以明确双方的责任。
规约可以隔离变化,无需通知客户端,即不管具体实现如何修改,只要符合规约,客户端就没必要知道修改的具体内容。
达到解耦的目标。
可以利用规约来判定方法的行为等价性。
测试用例可以根据规约来编写。(方法的)规范和合同
规格(或称为合同)
规范是团队合作的关键。没有规范就不可能委派实现方法的责任。
规范充当一个契约,实现者负责满足契约,而使用该方法的客户机可以依赖该契约。
–说明方法和调用方的职责
–定义实现正确的含义
规范对双方都有要求:当规范有前提时,客户也有责任。为什么需要规格br> 现实:程序中许多最糟糕的错误都是由于对两段代码之间的接口行为的误解而产生的。
–虽然每个程序员都有自己的规范,但并不是所有的程序员都把它们写下来。因此,团队中的不同程序员有不同的规范。
–当程序失败时,很难确定错误在哪里。
优点:代码中的精确规范可以让您将责任分摊到代码片段上,并且可以让您免去为修复应该去哪里而费解的痛苦。
–规范对于方法的客户端来说很好,因为它们省去了读取代码的任务。规范(合同)
该契约充当客户端和实现者之间的防火墙。
–它保护客户不受该单元工作细节的影响。
–它保护实现者不受单元使用细节的影响。
–此防火墙令客户端和程序不耦合,只要更改符合规范,允许独立更改单元代码和客户端代码。行为等价性
为了确定行为等价性,问题是我们是否可以用一个实现代替另一个实现规范结构:前置条件和后置条件
规格结构
方法的规范由几个子句组成:
–Precondition,由关键字requires指示
–Postcondition,由关键字effects指示
–Exceptional behavior:如果违反了Precondition,它会做什么
先决条件是客户的义务(即方法的调用方)。它是调用方法的状态的条件。
后置条件是方法实现者的义务。
如果调用状态的前提条件成立,则该方法有义务通过返回适当的值、抛出指定的异常、修改或不修改对象等方式遵守后条件。规格结构
整体结构是一个逻辑含义:如果在调用方法时前置条件保满足了,则在方法完成时后置条件必须满足。如果在调用方法时前置条件不成立,则实现不受后置条件的约束。–它可以做任何事情,包括不终止、抛出异常、返回任意结果、进行任意修改等。按照强弱分类
规约A强度>B规约强度意味着下面两点:A的前置条件更弱
A的后置条件更强
前置条件和后置条件的强弱由条件的严格程度决定,即如果条件越放松,那么这个条件越弱。反之亦然。如果要增强一个规约的强度,那么就意味着更松的前置条件+更严格的后置条件。
按照确定性分类
确定性规约(Deterministic):给定前置条件,其输出是唯一的、明确的。欠定的规约(Underdetermined):同一个输入可以有多个输出,但是一旦使用具体的方法实现了这个规约,那么这个返回值也将会被确定。
非确定的规约(Not deterministic):同一个输入,多次执行可能得到多个结果,比如涉及到随机数的方法。
按照陈述性分类
操作式规约:有具体的实现细节,如伪代码声明式规约:没有内部实现的描述,只有对输入输出的规定
声明式规约更有价值,更能应对变化。但是操作式规约能够方便开发。
6.抽象数据类型(ADT)
抽象数据类型与表示独立性:
能够分离程序中数据结构的形式和对其使用的方式ADT的特性:不变量、表示泄漏、抽象函数AF、表示不变量RI
基于数学的形式对ADT的这些核心特征进行描述并应用于设计中。
大纲:
1.抽象和用户定义类型
2.ADT 中的操作分类
3.抽象数据类型示例
4.ADT的设计原则
5.代表独立性 (RI)
6.用Java实现ADT概念
7.测试ADT
8.不变量
9. Rep不变和抽象函数
10.良性突变
11.记录 AF、RI 和 Rep接触的安全性
12.ADT 不变量替换前提条件AF: 满射、非单射、 未必双射
R中的部分值并非合法的,在A中无映射值表示不变性RI:某个具体的“表示”是否是“合法的”
也可将RI看作:所有表示值的一个子集,包含了所有合法的表示值
也可将RI看作:一个条件,描述了什么是“合法”的表示值ADT操作的四种类型:
1.Creators 构造器:
用于创建一个新的对象,主要表现为构造函数。典型的有Integer.valueOf()。2.Producers生产器:
通过接受同类型的对象以创建新的对象。典型的有String.contact()。(连接两个字符串,产生一个新的字符串)3.Observers观察器
获取ADT内部的某个信息,而不改变ADT的现有状态。典型的有List.size()。(返回int,不同于原类型)4.Mutators变值器(改变对象属性的方法)
改变对象内部的信息。典型的有List.add()。
通常为void,如果为void,则必然意味着它改变了某些对象的内部状态,也有可能返回非空类型(如容器类的put、add方法)以上四类方法接受的参数类型和产生的返回值类型关系如下:
表示空间R指的是开发人员实际实现时内部的值,而抽象空间A表示的是用户看到的和使用的值。开发人员更关注R,而用户更关注A。
7.面向对象的编程(OOP)
大纲:
OOP的基本概念:对象、类、属性、方法、接口和枚举
OOP 的独特特征
——封装与信息隐藏
——继承与重写
——多态、子类型、重载
——静态与动态分派
Java 中一些重要的 Object 方法
设计好的课程
OOP的历史
总结接口,抽象类,具体类
类:实现抽象数据类型ADT。
接口:确定ADT的规约。接口之间可以继承与扩展;一个类可以实现多个接口;一个接口可以有多种实现类。Interface和Class:定义和实现ADT
也可以不需要接口直接使用类作为ADT,既有ADT定义也有ADT实现。(更偏向使用接口来定义变量)抽象方法:只有定义没有实现。具有方法名字但是没有具体实现的方法,需要有关键字abstract
抽象类:具有抽象方法的类;抽象类不能实例化。(不能用new生成对象)一个类至少包含一个抽象方法。
抽象接口:只含有抽象方法的抽象类
具体类:其中的所有方法都必须实现!!
不变对象的引用等价性,对象等价性
不可变类型必须重写equals()和hashCode()。
可变对象的观察等价性,行为等价性
注意:如果某个mutable对象包含Set集合类中,当其发生改变后,集合类的行为不确定。
对可变类型,实现行为等价即可(equals()should implement behavioral equality)。就是说,只有指向同样内存空间的objects,才是相等的。
对可变类型来说,无需重写equals()和hashCode(),直接继承Object的两个方法即可
9.面向复用的软件构造技术
大纲:
什么是软件重用br> 如何衡量“可重用性”br> 可重用组件的级别和形态
——源代码级别的复用
——模块级别的复用:类/抽象类/接口
——库级别的复用:API/包
——系统级别的复用:框架
设计可复用的类
——继承与重写
——重载
——参数多态与泛型编程
——行为子类型和 Liskov 替换原则 (LSP)
——组合与委托
设计可复用库与框架
——API 和库
——框架
——Java 集合框架(一个例子)Programing for/with reuse
programming for reuse(面向复用编程):开发出可复用的软件
programming with reuse(基于复用编程):利用已有的可复用软件搭建应用系统优点:
- 很大的适应性
- 降低成本和开发时间
- 充分的测试→高可靠、稳定
- 标准化、在不同应用中保持一致
缺点:针对性不强→性能差(代价高)
白盒复用:源代码可见,可修改和扩展。(复制已有代码到正在开发的系统,进行修改)
优点:可定制化程度高
缺点:对其修改增加了软件的复杂度,且需要对其内部充分的了解
黑盒复用:源代码不可见,不能修改。(只能通过API接口来使用,无法修改代码)
优点:简单,清晰
缺点:适应性差些LSP
- 子类必须完全的实现父类的方法(不能删父类的方法;子类型需要实现抽象类型中的所有未实现的方法)
- 子类可以有自己的个性(子类型可以增加方法)
- 子类型中重写的方法必须使用同样类型的参数或者符合co-variance的参数(此种情况Java目前按照overload处理)
- 子类型中重写的方法不能抛出额外的异常(协变)
- 覆盖和实现父类的方法时输入参数可以被放大(更弱的前置条件/逆变)
- 覆盖和实现父类的方法时输出参数可以被缩小(更强的后置条件/协变)
- 更强/保持的不变量
协变、反协变
数组的子类型化
泛型的子类型化
泛型中的通配符(
可采用通配符实现两个泛型中的协变
只有在下面这种情况下可以用无限定通配符 br> 当实现某方法的代码不依赖于类型参数(不调用其中的方法),如List中的方法;或者只依赖于Object类中的功能,如toString(),HashCode(),Clone()…
下限通配符 <super A>:eg.List<super Integer>
上限通配符<extends A>:eg.List<exteds Number>Delegation
Comparator和Comparable
CRP原则
接口的组合
白盒框架的原理与实现
黑盒框架的原理与实现
10.面向可维护性的构造技术
大纲:
软件维护和演进
可维护性指标
模块化设计和模块化原则
面向对象设计原则:SOLID
基于语法的构建
——语法和解析器
——正则表达式(regexp)可维护性的常见度量指标
聚合度与耦合度
SOLID
语法、正则表达式
11.面向可复用性和可维护性的设计模式
大纲:
创作模式
——工厂方法模式在不指定确切类的情况下创建对象。
结构模式
——适配器允许具有不兼容接口的类通过将自己的接口包装在已经存在的类的接口上。
——装饰器动态添加/覆盖对象方法中的行为。
行为模式
——策略允许在运行时选择一系列算法中的一个。
——模板方法将算法的骨架定义为抽象类,允许其子类提供具体的行为。
——迭代器顺序访问对象的元素而不暴露它的底层表示。
——访问者通过移动对象结构将算法与对象结构分开方法的层次结构到一个对象中。设计模式adapter、decorator、strategy、template、iterator/iterable、factory method、visitor
12.面向正确性与健壮性的软件构造
大纲:
什么是稳健性和正确性br> 如何衡量稳健性和正确性br> Java 中的错误和异常
异常处理
断言
防御性编程
SpotBugs 工具
总结健壮性和正确性
Throwable
Error/Runtime异常、其他异常
Checked异常、Unchecked异常
Checked异常的处理机制:
声明、抛出、捕获、处理、清理现场、释放资源等
自定义异常类
断言的作用、应用场合
防御式编程的基本思路
参考:
link (2道大题)
link
link
link
link
link (18年部分答案)来源:-tenacity-
声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!