软件构造学习笔记(四)

第四讲 数据类型和类型检验

1 编程中的数据类型
Java中的数据类型可分为基本类型和对象类型,具体如下表所示:

 

所有的对象类型都由最初的类Object继承而来,形成层次结构。

基本类型有相应的对象类型(包装),一般可以自动转换。

2 静态/动态类型语言

Java是一种静态类型的语音,Java中所有的变量类型在编译时已知,因此编译器可以推导出所有表达式的类型

而类似python这类直到运行时才知道变量类型的语音被称为动态类型语言

2 静态/动态数据类型检测

一种语言所能提供的三种自动检查:

- 静态检查:在程序运行之前就能自动发现错误,因此避免了将错误带入到运行阶段,可提高程序正确性/健壮性。

可以发现语法错误、类名/函数名错误、函数的参数数目和类型错误、函数实现体中的返回值类型错误,是关于“类型”的检查。

- 动态检查:当代码被执行时会自动发现错误,可以发现非法参数值(如x/y, y=0)、非法返回值、数组越界、空指针引用等错误,是关于“值”的检查。

- 不检查。

毋庸置疑,静态检查比动态检查好,而动态检查又比不检查好。

3 不变性和可变性
不变性是一个重要的设计原则。

不变数据类型:一旦被创建,其值不能改变。

不变对象:一旦被创建,始终指向同一个值/引用。
可变对象:拥有方法可以修改自己的值/引用。

基本类型及其封装对象类型都是不可变的。

为了使参数不变,可以用关键字final修饰。

final可以限制一个不可变的/不可重新分配的对可变值的引用(ps:final StringBuilder sb)或可变的引用也可指向比可变的值。
在Java中,用final修饰的变量在首次赋值后发生改变,编译器在静态类型检查时会提示错误。

编程时应尽量使用final变量作为方法的输入参数、作为局部变量。

同时关键字final也代表了程序员的一种“设计决策”。

final类无法派生子类、final变量无法改变值/引用、final方法也无法被子类重写。

可以使用Collections.unmodifiableList()方法将可变类型包装为不可变类型。

String为不变类型,其相应的可变类型为StringBuilder.

使用可变类型的优势:

可变类型因为最少化拷贝,可以提高效率;

可变数据类型适合于在多个模块之间共享数据,可获得更好的性能。

使用不可变类型的优势:

不可变类型更“安全”,在其他质量指标上表现更好。

4 Snapshot图解
Snapshot图用于描述程序运行时的内部状态。

双线箭头指的是不可变的引用,双线椭圆指的是不可变的对象。

 

 

5 复杂数据类型
Array: 定长数组。

List: 顺序表,是一个抽象接口,有ArrayList等实现。

Set: 集合。

Map: 类似于字典的抽象接口,有HashMap等实现。

Iterator,迭代器,一个对象,遍历一组元素并逐个返回元素。
迭代器有两种方法,next()返回下一个元素,是可变方法;hasNext()是测试是否到达一组数据的结束的。

当添加一个item时,编译器执行静态检查,确保只添加合适类型的item。因此,可确保取出的值是指定类型的。

基本类型及其封装对象类型都是不可变的。Java的包装器得到的结果是不可变的,但是这种“不可变”是在运行阶段获得的,编译阶段无法据此进行静态检查。
不可修改的包装主要有两个用途:使集合在构建后保持不变;允许某些客户端以只读方式访问您的数据结构。

 

第五讲 设计规约
规约可以看做是用户和实现者间的防火墙。

1 规约的意义

规约可以隔离“变化”,无需通知客户端,也可以提高编码效率。

精确的规约,有助于区分责任。客户端无需阅读被调用函数的代码,只需理解规约即可。

根据规约可判断具体的实现是否行为等价。

2 规约的结构

规约只讲“能做什么”,不讲“怎么实现”。

前置条件:对客户端的约束,在使用方法时必须满足的条件。

后置条件:对开发者的约束,方法结束时必须满足的条件。

契约:如果前置条件满足了,后置条件必须满足。

静态类型声明是一种规约,可据此进行静态类型检查static checking.

方法前的注释也是一种规约,但需人工判定其是否满足。

 

根据规约可设计黑盒测试用例。

3 设计规约

规约确定了一个范围,如果某个具体的实现满足规约,则在其范围内;否则就在范围外。

程序员可以在规约范围内自由实现函数,而客户端无需了解具体如何实现。
规约越强,则确定的范围就越小,实现的自由度更低。

更强的后置条件意味着实现的自由度更低了在图中的面积更小

更弱的前置条件意味着实现时要处理更多的可能输入,实现的自由度低了面积更小

当然也会出现规约强度无法比较的情况,二者的规约范围可能有一定交集。
强的规约:更放松的前置条件+更严格的后置条件。

好的规约应该是内聚的(功能单一、简单、易理解)、信息丰富的、足够强的(有利客户端)、适当弱的(便于实现)。

在设计时应尽量使用抽象数据类型,提供更大的自由度。

私有方法可以使用前置条件,公有方法可以放松或不适用前置条件。

 

posted @ 2022-06-13 23:12  TongL_Roy  阅读(63)  评论(0)    收藏  举报