最近在复习软件构造的期末考试,在看这一讲关于ADT等价性的ppt时感到思路有些混乱,什么观察等价性,行为等价性,基于AF,Observation判断什么的。于是借着期末复习的机会在这里整理一下这方面的知识。
首先等价性要满足自反,传递,对称三个性质。如果连这都不能满足,那就肯定不等价。这方面的知识建议前往离散数学中回顾。
接下来介绍两种判断等价性的方法:基于AF判断和基于Observation(站在外部观察者角度)判断。
1.两种判断等价性的方法
基于AF判断:
如果不理解AF是什么可以前往这里学习
用AF来判断等价很简单,即如果AF(a)==AF(b),那么a与b等价,这要求AF映射到同样的结果。
基于Observation(站在外部观察者角度)判断:
如果对两个对象调用任何相同的操作(如果有的话)都会得到相同的结果,那么二者等价。
下面是几个例子。
例1
给定以上Duration类,那么下面几个对象哪两个之间可以被认为是等价的?
首先从AF的角度。Duration的AF为represents a span of time of mins minutes and secs seconds意思就是它是一个从(mins,secs)到time(时间段长度)的映射。那我们判断映射到的时间长度是否相等不久可以判断两个对象是否等价了吗?显然d1,d3,d4是等价的。
其次从Observation角度。正如上文所说,针对两个对象,我们需要调用所有方法来测试他们,如果他们都返回相同的结果,那么等价。在这个问题里,我们只拥有getLength这一个方法(构造方法是不算的)。于是我们对这四个对象调用getLength方法,发现d1,d3,d4的返回值相同。于是这三个等价。
所以在这个例子里,AF和Observation两种方法是一致的。后面你会发现这两种方法并不总是一致的。
例2
在LetterSet类中,除了构造方法,没有其他任何方法,所以我们只能应用AF来判断等价性 。AF的意思是一个字符串映射到一个由该字符串中的字母组成的集合(忽略大小写)。所以“abc”,"aBc"和"1a2b3c"是等价的,因为他们映射到的A空间中的值相同。
例3
这次变成没有AF了,那么我们就用Observation来判断等价性。即利用slope方法测试每一个对象,看他们的返回值是否相同。经检验,第1,2,3,5个对象返回的斜率都是1,所以他们4个是等价的。
2.观察等价性与行为等价性
这部分知识其实很多,建议去看课堂ppt。这里就用语言概括一下并给出简单例题。
观察等价性:在不改变状态的情况下,两个对象是否看起来一致。即不使用mutator方法检验。
行为等价性:调用任何方法,对象展示出的结果都一样。这次要使用mutator检验。
这里就可以看出,对于不可变对象,观察和行为等价性是一致的,因为不可变对象没有mutator(变值器)。
我的理解就是,观察等价性看内部逻辑,行为等价性看对象指向的地址是否相同(如果理解“行为”二字?两个对象指向同一片地址空间,那么对两个对象做任何动作,他们都是相等的)
在操作上:
不可变数据类型必须重写equals和hashcode方法,可变数据类型不应该重写equals和hashcode方法。
注:可变数据类型实现行为等价性就好,equals()和hashcode()直接从object继承,无需重写。因为可变数据类型要求实现行为等价性(就是地址一样),而object里的equal()就是这么编写的。
下面给出一个例题。有如下几个方法。
下面哪些判断是正确的?
要解决这道题,我们最好画出snapshot图。
可以看出,b1,b3指向同一片内存空间,都为"a"。b2为"ab" ,b4为“ab”(这个不分先后,只要包含就好)。所以如下图所示,标红的为错误的。
如果用行为等价性来判断,那么只有b1和b3指向同一片内存空间。
所以如下图所示,标红的正确。
如果用观察等价性来判断,那看的就是内部逻辑。
所以如下图所示,标红的正确。
最后我们再看一个例子。
显然,由于Integer为不可变类型,它的equals方法已经被重写,所以x.equals(y)返回true。
那若是 x==y 呢?显然返回false,因为x和y指向两段不同的地址空间。
那如果是(int) x==(int) y 呢?这回就是对的了。因为基本数据类型(int,double等)一个值对应一个地址空间。x和y被转换为int型后,由于他们代表的都是3,所以都指向3这个内存空间。