写在前面的话
这个系列的blog一部分是课程要求,一部分是我自己的复习总结,希望能对自己和同学们的考前复习有一点帮助吧。
ADT的定义
ADT,抽象数据类型,一个数学模型以及定义在该模型上的一组操作。
看到一个博主比较形象的理解,他用买房子来类比ADT
类比于买房子来理解接口类及其实现类:
接口类:
接口类就是房户,接口里面的方法就是房户里面规定好的空的房间,比如卫生间,客厅,但这些房间都是空的,即接口里面这些方法就是空的框架,没有实现。而每个方法的注释,就相当于告诉你这个房间是干啥的,可以放啥东西。
好了,现在这个房户有了,但是还没有人买并住下去,即这个接口类还没有被实现。没有人住的房子,是没有一个没有灵魂的空壳,只有人买了它,它才会华丽变身成一个有灵魂的房子,于是这个接口类(空房子)就有了实现类(有人的房子)
实现类:
实现类就是相当于一个住了人的房子。 我们买一个空房子,就相当于为这个房子注入灵魂,实现这个接口类
首先,我们需要办理房产证,相当于,在实现类中添加构造器(接口类没有构造器,就相当于空房子没有房产证)
接着,我们看到了那些空的房间,于是我们开始往里面添加应该在这个房间里的家具(参数),让这个房间充分实现它的价值。相当于在实现类中@Override
重写接口类的方法,使其完整实现。 我们必须把所有房间都装修完成,即所有接口类中的方法都要重写。
突然,在我们装修一个卧室时候,我们觉得这个房间太小了,不能做卧室,要改成书房。于是,我们决定这个房间正式变为书房,并且往里面添加书房家具而不是卧室家具。也就是说,我们@overload
重载了接口类中的这个方法 好了,看着装修好的三室一厅,我们心满意足地叹口气。
然而,我们惊奇地发现自己的杂物没有地方放了,原来的房间不够了。于是我们灵机一动,开辟了一个新的储藏室来储存我们的东西。这就相当于在实现类中添加方法。终于,我们把我们的房子装修好了,也就是我们实现类写好了。我们可以开开心心地住进去了(使用实现类)。
显然这种简单具体的理解帮助我们很好的理解了ADT,那么我们就来用专业一点的术语来解视一下什么叫ADT。
ADT(Abstract data type),即抽象数据类型,可以简单理解为实现了一组操作的数据类型,与基本数据类型不同,抽象数据类型强调作用在数据上的操作,而不是数据的具体表示。
它的作用是可以使我们更容易描述现实世界。例:用线性表描述学生成绩表,用树或图描述遗传关系。
为什么这么说呢,因为使用ADT有这么一些关键因素:
使用它的人可以只关心它的逻辑特征,不需要了解它的存储方式。定义它的人同样不必要关心它如何存储。
也就是将两个部分的设计者工作分割开来了,这样就方便了一个Team更好实现一个工程。
表示泄露
解释完ADT之后,我们来实正式了解什么叫表示泄露。
要说表示泄露,又不得不提一嘴表示独立性。
表示独立性
先说什么是representation(表示),表示可以简单理解为数据成员(域),那么表示独立性是什么?
Representation Independence
client使用ADT时无需考虑内部如何实现,ADT内部表示的变化不应影响外部spec与client除非ADT的操作指明了具体的pre-condition和post-condition,否则不能改变ADT的内部表示,因为spec规定了client和和implementer之间的契约。
表示泄露
那表示泄露rep exposure又是什么呢?
一个简单的小例子,相信大家就能看明白了。
public class mySet {
private final Set<String> set = new HashSet<>();
public mySet() {}
public Set<String> getSet() {
return set;
}
public boolean addToSet(String add) {
return set.add(add);
}
}
首先来看这个ADT,我仅仅给这个类赋予两种方法和一个构造器,也就是说,按照我们的契约,用户只能使用两种方法来操作这个类。
但是用户完全可以通过下面的方法对这个类的元素进行操作。
mySet s = new myset();
s.getSet().add("Whatever I wanna add");
那这显然是不符合我们之前所约定的用户与implementer之间的契约,用户跳过了implementer的约定直接操作了ADT的集合,这就是表示泄露。
如何防止
那么如何防止表示泄露呢?
我们可以进行防御式拷贝。
具体什么意思呢?不如就让我们依然用上面那个Class进行举例,我们要如何修改才能让用户不能越过契约直接操作Set呢?
很简单,可以这么修改:
public class mySet {
private final Set<String> set = new HashSet<>();
public mySet() {}
public Set<String> getSet() {
return new HashSet<>(set);//以set的为基础拷贝了一个新的HashSet作为返回值返回
public boolean addToSet(String add) {
return set.add(add);
}
}
那么很显然,无论用户如何进行操作,操作的都是那个新的Set而不是我们的集合,就不会表示泄露了。