RI
RI(Representation invariant):表示不变量,即类中属性必须满足的那些条件。
eg1:
构建一个Person类的时候,我们并不希望一个人的名字为null,或者他的年龄为负数,所以我们可以写下对应的RI:
public class Person{
private String name;
private Integer age;
/*
RI:
name != null
age >= 0
*/
}
eg2:
Lab2中Edge类的实现(有向图的一条有向边),在spec中要求了每条边的起点和终点必须是非空的,并且这条边的权重必须为正。所以可以写下对应的RI:
class Edge<L>{
private final L source;
private final L target;
private final int weight;
/*
RI:
source != null;
target != null;
weight > 0;
*/
}
RI小结:
①为什么要写RI:
一个类中有许多属性,他们必须满足一些特定的条件以防止程序运行时出现一些错误。写下RI可以告知这个类的使用者需要注意哪些地方,还可以根据RI编写checkRep函数来检查这些不变量。
②RI限定了类属性的取值:
例如Person类中name可以是任意的字符串(包括空串),但是我们不希望name为空,所以人为限定了 name != null。 这时,name的取值集合就不再是任意字符串,而变成了除了空串以外的字符串,这个集合显然是所有字符串集合的一个子集。(设所有字符串的集合为U,满足RI的name的取值集合为A,那么显然 )
③:RI就是一个条件:
这个条件是开发人员自己确定的,它与这个抽象类的使用场景有关。这个条件一旦确定就不能再改变(毕竟它的名字叫表示不变量)。这个条件是“对各个类属性的合法取值的一种描述”。符合RI描述的取值为合法的,否则是不合法的。
checkRep()
这是一个用来检查表示不变量(RI)的函数。一般使用断言来检查,这样在程序正式发布的时候,可以控制JVM中的开关来选择是否执行这些断言。
eg1:
public class Person{
private String name;
private Integer age;
/*
RI:
name != null
age >= 0
*/
private void checkRep(){
assert name != null;
assert age >= 0;
}
}
eg2:
class Edge<L>{
private final L source;
private final L target;
private final int weight;
/*
RI:
source != null;
target != null;
weight > 0;
*/
private void checkRep(){
assert source != null;
assert target != null;
assert weight > 0;
}
}
由上面两个例子可以看到,checkRep()就是按照RI的内容来写的。在creator,producer,mutator这些类型的方法中调用checkRep方法来检查RI是否还保持不变,如果断言没通过则是发生了错误。
AF
AF(Abstraction function):抽象函数,类似于一个映射。
说明:集合A为抽象空间(Abstract) ,集合R为表示空间(Rep)。
集合A是抽象值构成的空间:即client看到和使用的值。
集合R是表示值构成的空间:即实现者看到和使用的值。
用户(client)只关注抽象空间A,而ADT的实现者需要关注两个空间。(用户只用知道怎么用即可,具体内部怎么实现不需要关心。而ADT实现者必须对两个空间有清晰的把握)
如上图所示,这就是 “构造的程序的 ADT内部的量” ——> “客户想要操作的量” 这样一个映射。
AF一定是满射,不一定是单射,因此不一定是双射。
也就是R中的对象不一定能找到A,但是A中的抽象必须有R中对应的对象。(类比:客户要求的功能你必须要实现,但是你另外多写了什么没有关系)
What determine AF and RI?
eg:
这个ADT是要表示一条推文:
public class Tweet{
private final String author; //作者
private final String text; //内容
private final Date timestamp; //时间戳
/*
Rep Invariant:
author is a Twitter username(a nonempty string of letters,digits,underscores)
text.length <= 280
Abstraction Function:
AF(author,text,timestamp)=a tweet posted by author,with content text,
at time timestamp
*/
}
Rep Exposure
Rep Exposure:表示泄露。
java中数据类型可以分为mutable和immutable两种。对前者进行的操作会直接改变其内部数据,而对后者的操作不是改变其内部值,而是构造新的对象。因此,对于mutable的数据,如果没有良好的保护,意味着client对其的调用可以直接修改内部数据。
mutable类中可以直接用setter对其相应的内部值进行修改,而且这种修改是对类中变量指向的地址的内容进行修改,这就会导致表示泄露。应对此情况的方法是,getter得到的不应该是类中的变量,而是类中变量的一个副本,这也就是常说的防御式拷贝。
防止表示泄露通常有以下几种方法:
1. 使用private和final关键字修饰:------即将类中所有的属性(变量)定义为private类型,目的是不让用户得到你的内部属性。final使属性不能被修改和继承。
2. 尽量使用immutable数据类型,比如能使用String就不使用StringBuilder,能使用Instance或
LocalDateTime就不使用Data