软件设计师知识点:面向对象语言概论(二)

软件设计师知识点:面向对象语言概论(二),第1张

软件设计师知识点:面向对象语言概论(二),第2张

4. Subsumption和Dynamic Dispatch (译者按:呵呵,黔驴技穷,找不到合适的翻译了)

从上述的几个例子来看,似乎子类只是用来从父类借用一些定义,以避免重复。但是,当我们考虑到subsumption, 事情就有些不同了。什么是Subsumption呢?请看下面这个例子:

var myCell: InstanceTypeOf(cell) := new cell;

var myReCell: InstanceTypeOf(reCell) := new reCell;

procedure f(x: InstanceTypeOf(cell)) is … end;

再看下面这段代码:

myCell := myReCell;

f(myReCell);

在这两行代码中,头一行把一个InstanceTypeOf(reCell)类型的变量赋值给一个InstanceTypeOf(cell)的变量。而第二行则用InstanceTypeOf(reCell)类型的变量作为参数传递给一个参数类型为InstanceTypeOf(cell)的函数。

这种用法在类似Pascal的语言中是不合法的。而在面向对象的语言中,依据以下的规则,它则是完全正确的用法。该规则通常被叫做subtype polimorphism, 即子类型多态(译者按:其实subtyping应该是OO语言最区别于其它语言的地方了)

如果c’是c的子类,并且o’是c’的一个实例,那么o’也是c的一个实例。

更严格地说:

如果c’是c的子类,并且o’: InstanceTypeOf(c’),那么o’: InstanceTypeOf( c ).

仔细分析上面这条规则,我们可以在InstanceTypeOf的类型之间引入一个满足自反和传递性的子类型关系, 我们用<:符号来表示。(译者按:自反就是说, 对任何a, a 关系 a都成立,比如说,数学里的相等关系就是自反的。而传递性是说,如果a 关系 b, b 关系c, 就能推出a 关系c。 大于,小于等关系都是具备传递性的)

那么上面这条规则可以被拆成两条规则:

1. 对任何a: A, 如果 A <: B, 那么 a: B.

2. InstanceTypeOf(c’) <: InstanceTypeOf(c) 当且仅当 c’是c的子类

第一条规则被叫做Subsumption. 它是判断子类型(注意,是subtype, 不是subclass)的标准。

第二条规则可以叫做subclassing-is-subtyping (子类就是子类型,绕嘴吧?)

一般来说,继承都是和subclassing相关的,所以这条规则也可以叫做:inheritance-is-subtyping (继承就是子类型)

所有的面向对象语言都支持subsumption (可以说,没有subsumption, 就不成为面向对象)。

大部分的基于类的面向对象语言也并不区分subclassing和subtyping. 但是,一些最新的面向对象语言则采取了把subtyping和subclassing分开的方法。也就是说,A是B的子类,但A类的对象却不可以当作B类的对象来使用。(译者按:有点象C++里的私有继承,但内容比它丰富)

好吧,关于区分subclassing和subtyping, 我们后面会讲到。

下面,让我们重新回头来看看这个procedure f. 在subsumption的情况下,下面这个代码的动态语义是什么呢?

Procedure f(x: InstanceTypeOf(cell)) is

x.set(3);

end;

f(myReCell);

当myReCell被当作InstanceTypeOf(cell)的对象传入f的时候,x.set(3)究竟是调用哪一个版本的set方法呢?是定义在cell中的那个set还是定义在reCell中的那个呢?

这时,我们有两种选择,

1. Static dispatch (按照编译时的类型来决定)

2. Dynamic dispatch (按照对象运行时真正类型来决定)

(译者按,熟悉C++的朋友们一定微笑了,这再简单不过了。)

static dispatch没什么可说的。

dynamic dispatch却有一个有趣的属性。那就是,subsumption一定不能影响对象的状态。如果你在subsumption的时候,改变了这个对象的状态,比如象C++中的对象切片,那么动态解析的方法就可能会失败。

好在,这个属性无论对语义,还是对效率,都是很有好处的。

(译者按,C++中的object slicing会把新的对象的vptr初始化成它自己类型的vtable指针, 所以不存在动态解析的问题。但实际上,对象切片根本不能叫做subsumption。

具体语言实现中,如C++, 虽然subsumption不会改变对象内部的状态,但指针的值却是可能会变化的。这也是一个让人讨厌的东西,但 C++ vtable的方案却只能这样。有一种变种的vtable方法,可以避免指针的变化,也更高效。我们会在另外的文章中阐述这种方法。)

5. 赛翁失马 (关于类型信息)

虽然subsumption并不改变对象的状态,在一些语言里(如Java), 它甚至没有任何运行时开销。但是,它却使我们丢掉了一些静态的类型信息。

比如说,我们有一个类型InstanceTypeOf(Object), 而Object类里没有定义任何属性和方法。我们又有一个类MyObject, 它继承自Object。那么当我们把MyObject的对象当作InstanceTypeOf(Object)类型来处理的时候,我们就得到了一个什么东西也没有的没用的空对象。

当然,如果我们考虑一个不那么极端的情况,比如说,Object类里面定义了一个方法f, 而MyObject对方法f做了重载,那么, 通过dynamic dispatch, 我们还是可以间接地操作MyObject中的属性和方法的。这也是面向对象设计和编程的典型方法。

从一个purist的角度看(译者按,很不幸,我就是一个purist), dynamic dispatch是你应该用来操作已经被subsumption忘掉的属性和方法的东西。它优雅,安全,所有的荣耀都归于dynamic dispatch!!! (译者按,这句话是我说的)

位律师回复
DABAN RP主题是一个优秀的主题,极致后台体验,无插件,集成会员系统
白度搜_经验知识百科全书 » 软件设计师知识点:面向对象语言概论(二)

0条评论

发表评论

提供最优质的资源集合

立即查看 了解详情