前言:ADT和OOP是本节课学习的重点之一。本文拟总结本章学习心得。
文章目录
一、ADT
1. 如何设计ADT?
设计ADT:设计规约Spec->表示不变性Rep->实现Impl
1.1 规约Spec的写法
- 方法注释
- @param 参数说明
- @return 返回值说明
- @throw 抛出异常
1.2 规约Spec的强度和评价
1.2.1 规约Spec的强度:
- 前置条件越弱,规约强度越强;
- 后置条件越强,规约强度越强;
- 其他情况无法比较。
1.2.2 规约Spec的评价:
- 内聚的:一个好的规约Spec应该功能单一,易于理解;
- 信息丰富的:不产生歧义;
- 足够“强”的:使开发人员应该考虑足够多的异常情况并进行处理;
- 足够“弱”的:为开发人员减轻工作负担并降低开发成本。
1.3 抽象函数AF和表示不变量RI
1.3.1 抽象函数AF:
- 表示R(表示空间)和A(抽象空间)之间关系的一个映射;
- AF:R->A;
1.3.2 表示不变量RI:
- 描述什么是合法的表示值;
- 是所有表示值的一个子集。
1.4 ADT实现Impl(表示泄露)
通过防御式拷贝,给客户端返回一个全新的对象,即使客户端修改了数据,也不会影响自己。然而大量的拷贝会占用内存空间,因此很多时候会使用不可变数据类型以节省频繁复制的代价。
2. ADT测试Test
需要注意的是,由于测试时开发者也相当于用户,因此不能直接访问ADT内部的数据域,只能调用其他方法测试待测的方法。
- 针对creater:构造对象后,用observer观察是否正确;
- 针对observer:用其他方法构造对象,调用被测observer,观察判断结果是否正确;
- 针对producer:produce一个新的对象,用observer观察结果是否正确。
二、OOP
1. OOP的基本概念
1.1 接口(Interface)
接口是一系列方法的声明,是一些方法特征的集合。
- 接口之间可以继承与扩展;
- 一个类可以实现多个接口;
- 一个接口可以有多个实现类。
1.2 抽象类
在类中只有一些方法声明,没有具体的方法(即未被实现,等待子类实现)。
- 抽象类至少有一个抽象方法
1.3 类
每个类包含数据说明和一组操作数据或传递消息的函数。
- 类必须实现抽象类中所有未被实现的方法。
1.4 继承(Inheritance)与重写(Override)
类与类之间是继承与被继承的关系;
类与接口是实现与被实现的关系。
重写:
- 参数列表必须与父类完全相同;
- 访问权限不能比父类中被重写的方法的访问权限更低;
- 声明为 final 的方法不能被重写;
- 构造方法不能被重写;
- 当需要在子类中调用父类的被重写方法时,要使用 super 关键字。
1.5 多态(Polymorphism)与重载(Overload)
多态:
多态是同一个行为具有多种不同表现形式或形态的能力。
- 可以消除类型之间的耦合关系;
- 可替换,可扩充;
- 灵活性,简化性。
重载: - 多个方法具有相同的名字,但具有不同的参数列表或返回值类型;
- 无法以返回值类型作为重载函数的区分标准;
- 被重载的方法可以声明新的或更广的异常。
三、 ADT和OOP中的等价性
1. 不可变对象
1.1 引用等价性与对象等价性
- “==”是引用等价,即两个引用指向了相同的内存空间;
- “equals()”是对象等价,即两个对象的域相同。
1.2 如何判断两个不可变对象相等
如果AF映射到同样的结果则等价。在自定义ADT中,如果要判断等价,需要重写equals()方法和hashCode()方法。
2.可变对象
2.1 观察等价性与行为等价性
- 观察等价性:在不改变状态的情况下,两个可变对象看起来是否一致;
- 行为等价性:调用对象的任何方法表现出一致的结果。
对可变数据类型,往往倾向于实现严格的观察等价性,但有些时候可能导致bud,甚至破坏RI。对于可变数据类型,无需重写equals()f方法和hashCode()方法,直接继承自Object即可,而如果一定要判断两个可变对象看起来是否一致,最好定义一个新的方法。
3.总结
- 等价性应该是一种等价关系;
- 抽象函数AF是等价性的基础。