属性也应该是接口- -| 回首页 | 2005年索引 | - -为goto正名

相等性判断的自动化- -

                                      

在面向引用的OO编程语言中,相等性(Equality)操作有两种:
1、判断两个引用本身是否相等;
2、判断两个引用所引用的对象是否相等。

事实上,第二种操作是第一种操作的超集。因为,如果两个引用本身相等,则两个对象一定是相同的,当然也是相等的;但反之则不然。

对于第一种操作,执行起来非常简单。但第二种操作则相对复杂,需要程序员的指定或干涉。在Java中,具体表现为程序员需要为一个class编写equals方法。

两个对象本身的相等性应该具备下列性质:
o Reflexive:对象应该等于自身,也就是第一种相等性操作;
o Symmetric:对称性,即 a equals b 意味着 b euqals a;
o Transitive:传递性,如果 a equals b,b equals c,则a equals c;
o Consistent:一致性,在对象内容没有变化的情况下,两个对象之间进行多次比较时,结果应该是一致的。

对象本身的相等性判断,是高度模式化的操作。两个对象之间的相等性,在绝大多数情况下,其实是对象所包含的部分或全部属性之间的相等性。基于这种现实,我们应该把相等性判断的操作交给编译器去做。

首先,并非所有的class都需要进行对象内容的相等性判断,所以程序员可以指定一个class是否是compareable的,像这样:

public compareable class Foo{
   ...
}

编译器只对声明了compareable的class生成内容相等性判断的代码。如果目标代码是Java的话,则只为声明为compareable的class生成equals方法。对于这种class的对象,就可以对其进行内容相等性判断,比如:

Foo foo1 = new Foo();
Foo foo2 = new Foo();

if(foo1 == foo2) // 引用相等性判断,返回false
   ...

if(foo1 equals foo2) // equals是关键字,用以进行内容相等性判断
   ...

只有声明为compareable的类及其子类的对象,才能进行equals操作。编译器会检测这一点。

其次,在对两个compareable class的对象进行内容相等性判断时,并非所有属性都需要对比。所以应该允许程序员指定需要对比的属性。像这样:

public compareable class Foo {
   private compareable int value;
   private compareable string name;
   private int dummy;

   ...
}

在这个例子中,在对两个Foo对象进行相等性判断时,应该仅仅对value和name的值进行对比,由于dummy不是compareable的,所以无需对其进行相等性判断。

如果一个属性被指定为compareable的,则属性所属的类型必须是compareable的。否则编译器需要给出错误。对于基本类型,比如int, boolean, string, real等,默认就是compareable的,由于object是所有class的root class,所以object不是compareable的。比如:

public compareable class Foo {
   private compareable int value; // valid
   private Exam exam;   // Invalid, because class Exam isn't compareable.
}

public class Exam {
   ...
}

对属性进行对比并不完全是对属性对象内容本身进行对比,有时候,如果属性是引用的话,只需要对引用进行对比就行了,对于这种情况,我们可以使用identical来声明。例如:

public compareable class Foo {
   private compareable int value; // valid
   private identical Exam exam;  // valid
}

public class Exam {
   ...
}

对于identical属性所属的类型,不需要是compareable的。另外,对于非引用类型的基本类型,既可以使用compareable来修饰,也可以用identical来修饰,效果上是一致的。但二者不可以同时使用。

如果一个class的super class是被声明为compareable,则其默认就是compareable的;其相等性判断算法等同于super class。比如:

public compareable class Base {
   public compareable int value = 10;
}

public class Derived extends Base{
}

Base base = new Base();
Derived derived = new Derived();

if(base equals derived) // return true
   ...

但是,如果它想扩展super class的相等性判断算法,仍然必须明确声明为compareable的。比如:

public compareable class Base {
   public compareable int value = 10;
}

// Invalid,因为它新指定了属性name为compareable的,
// 所以Derived也必须被明确指定为compareable的。

public class Derived extends Base{
   public compareable string name;
   ...
}

还有一种情况,如果你想让一个类在内容相等性上区别于它的父类,也可以通过明确指定自身为compareable的来完成。例如:

public compareable class Base {
   public compareable int value = 10;
}

public compareable class Derived extends Base{
}

Base base = new Base();
Derived derived = new Derived();

if(base equals derived) // return false,既然Derived也有自己的compareable声明。
   ...

对于集合性质的属性,如果被指定为compareable的,首先集合元素的类型应该是compareable的,其次,集合内元素的数量如果不相等,则两个集合肯定是不相等的。然后需要比较集合内的元素,此时,需要分为不同的情况来处理。

集合通过两种属性(ordered, unique)分为四类:
1、Set (non-ordered, unique)
2、OrderedSet (ordered, unique)
3、Bag (non-ordered, non-unique)
4、Sequence ( ordered, non-unique)

首先如果集合的类型如果不相同,则两个集合肯定是不相等的。

o 对于Set,我们通过判断一个Set中的元素是否包含在另外一个Set中来确定其相等性。
o 对于OrderedSet和Sequence,我们根据顺序依次判断两个集合中元素的相等性。
o 对于Bag的处理要相对复杂一些,我们首先对一个Bag进行过滤操作,把其重复的元素去掉,然后计算每一个非重复元素的数量,得到结果后,通过判断另外一个Bag中的非重复元素的数量是否匹配,得出相等性。

以上所描述的方法都是模式化的,这些都可以利用编译器进行自动代码生成。但有时候,相等性判断是非常特殊的操作。比如下面的Java代码:

public class Foo {
   private int value;
   private string name;

   public boolean equals(Object object) {
      ...
      Foo foo = (Foo)object;
      if(value < 0) {
         if(foo.value > 0)
            return false;
      }
      else {
         if( foo.value != this.value)
            return false;
      }

      return foo.name.equals(this.age);
   }
   ...
}

对于这些特殊情况,我们应该允许程序员来自定义特殊的相等性判断函数。可以像这样:

public compareable class Foo {
   private int value;
   private compareable string name;

   equals {
     this.value < 0 implies rhs.value < 0;
     this.value > 0 implies this.value == rhs.value;
   }

   ...
}

在我们的解决方案中,我们仍然可以指定那些无需特殊处理的属性为compareable的,对于特殊处理的条件,我们可以在equals block中,通过OCL boolean表达式来指定判断条件。在这些表达式都满足的情况下,再对指定为compareable的属性进行默认的相等性判断,以最后的结果来决定两个对象之间的相等性。

我们希望能够尽量避免让程序员编写模式化的操作,通过这些解决方案,应该可以很好的解决相等性模式代码生成的问题。

- 作者: 上帝没发笑 2005年01月31日, 星期一 04:33 加入博采

Trackback

你可以使用这个链接引用该篇文章 http://publishblog.blogchina.com/blog/tb.b?diaryID=650140

回复

评论内容: