软件构造Lab2的主题也是我们课程的重点,ADT and OOP,即Abstract Data Type抽象数据类型和Object-Oriented Programming面向对象编程。
本次实验训练抽象数据类型(ADT)的设计、规约、测试,并使用面向对象编程(OOP)技术实现 ADT。具体来说:
⚫ 针对给定的应用问题,从问题描述中识别所需的 ADT;
⚫ 设计 ADT 规约(pre-condition、post-condition)并评估规约的质量;
⚫ 根据 ADT 的规约设计测试用例;
⚫ ADT 的泛型化;
⚫ 根据规约设计 ADT 的多种不同的实现;针对每种实现,设计其表示(representation)、表示不变性(rep invariant)、抽象过程(abstractionfunction)
⚫ 使用 OOP 实现 ADT,并判定表示不变性是否违反、各实现是否存在表示泄露(rep exposure);
⚫ 测试 ADT 的实现并评估测试的覆盖度;
⚫ 使用 ADT 及其实现,为应用问题开发程序;
⚫ 在测试代码中,能够写出 testing strategy 并据此设计测试用例。
与实验一不同,实验二的难度显然上升了一个层级,首先给出了一个接口(Interface)
接口类的定义是:
1.接口是一种引用数据类型接口是完全抽象的,抽象类是半抽象的,接口也可以说是特殊的抽象类
2.接口语法 [修饰符列表] interface {}
3.接口可以继承多个接口
4.接口中只包含常量和抽象方法
5.接口中抽象方法的public 和 abstract可以省略
6.接口中的方法都是抽象方法,所以不能有方法体
7.接口中的常量可以省略public static abstract 例 double PI = 3.14;
8.接口中的方法访问权限为public,实现接口类的方法访问权限也必须为public,不能更低
9.接口和接口之间没有继承关系也可以进行强制类型转换,但运行时可能会出classcastexception异常
10.接口和继承同时存在时,extends在前Implements在后
我所理解的接口类就是给出一个模板,所有使用这个模板的实现类(implements)都要完全重写接口类的抽象方法,并要拥有接口类提供的常量。此外,一个接口类可以有多个实现类,一个子类也可以实现多个接口。
接口类的好处在于他可以提醒程序员应该提供什么样的方法,严格按照接口类的模板构建方法。
那么在Lab2中我们构建的接口类是要完成一个抽象数据类型,即图的构建。那么我们设计两个实现子类,分别以点为构建对象构建图,和以边为构建对象构建图,但都作为子类继承实现我们定义的接口类。也要再此基础之上构建自己的点类、边类。
在构建实现子类之前,我们要遵循测试优先的原则先编写测试类。由于我们已经提供类接口类,给出了每个抽象方法的规约,我们以黑盒测试的方式,在不知道方法具体内容的前提下构造应该实现的测试。
实验开始要求我们对程序设计的规范化:要求注明AF、RI以及避免产生rep exposure的方法的信息,要求设计出合理的检查函数checkRep(),要求设计出合理的构造器、观察器,要求重写equals方法、hashCode方法以及toString方法。
因此我们提出了final、private来创建变量,并生成其相应的构造器、观察器来对内部数据进行简单封装,进而达到防止数据泄露的目的。那么我们就着重了解一下final、private的作用。
private关键字 | 只在本类中可以使用 |
默认 | 在本类和本包中可以使用 |
protected关键字 | 在本类、本包和不同包的子类中可以使用 |
public关键字 | 在所有的类和包中可以使用 |
也就是说,当我们把数据类型定义为private后,用户就不能在外部直接更改数据造成不必要的数据溢出或访问。这会比定义在Public中的数据安全得多。
接下来是final关键字,一旦将一个方法或数据类型以final修饰,那么无论是内部还是外部,被修饰的方法不能重写,被修饰的变量不能再次赋值。但这里有一个比较特殊的点,数据类型分为mutable和immutable
可变类型的对象:提供了可以改变其内部数据值的操作,其内部的值可以被重新更改。
不可变数据类型:其内部的操作不会改变内部的值,一旦试图更改其内部值,将会构造一个新的对象而非对原来的值进行更改。
例如经典例子String就是不可变数据类型
toLowerCase()方法不会改变s中包含的数据“ABC”。而是创建一个新的String对象并将其初始化为“abc”,然后返回这个新对象的引用。
尽管String类声明中没有提供让它成为不可变对象的语法,但是,String类的方法中没有方法去改变一个String包含的数据,这就使得它是不可变的。
mutable | immutable | |
优 点 | 可变类型会减少数据的拷贝次数,从而其效率 要高于immutable | 内部数据的不可变导致其更加安全,可以用作多线程的共享对象而不必考虑同步问题 |
缺 点 | 可变类型由于其内部数据可变,所以其风险更大 | 由于内部数据不可变,所以对其频发修改会产生大量的临时拷贝,浪费空间 |
而final在修饰这两种局部变量时,对于不可变数据类型,如Integer、Double、String、Float等,代表的意思是其存储的数据不可更改,而对于可变数据类型如Set、Map、List等则表示其地址不可变,但其内容数据仍可进行修改。
在简单完成子类的构造后,我们获得了两种基于不同实现的图ADT,那么我们可以将其视为一个类似Map、List一样的数据类型进行声明。