第2 继承

前面已经说过了,继承至少有2个语义: 实现继承和类型继承,在说明这两个东西之前,我们继续来看上面的例子

Person>>playSoloOnInstrument:instrument
  instrument playNote: Note C;
             playNote: Note D;
             playNote: Note E.

Person
>>playBackgroundOnInstrument:instrument
  instrument playChord: Chord C1;
      playChord: Chord C1;
             playChord: Chord C1;

Person
>>playBWV996OnInstrument:instrument
   instrument playNote: Note C;
              playChord: Chord C;
              playNote: Note D.

现在我们看playBWV996这个消息,BWV996是Bach所写一个鲁特琴组曲,鲁特琴是弹拨乐器,同时也是和声乐器(所谓和声乐器就是可以演奏和声)。在很多乐器上都有改编的版本,比如鲁特琴的近亲吉他等等,这个时候,我们可以实现这样几个类

Lute>>playNote:note
  
Lute
>>playChord:note
  

Guitar
>>playNote:note
  
Guitar
>>playChord:note
  

Bass
>>playNote:note
  

然后我们可以尝试以此调用

vincent.playBWV996OnInstrument: Guitar new.
vincent.playBWV996OnInstrument: Lute 
new.
vincent.playBWV996OnInstrument: Bass 
new.

最后一个会得到一个messageNotUnderstand的错误。也就是说,对于Bass而言由于不能演奏和声从而不能演奏BMV996(不过这个世界上能人太多了...哎),我们换到静态类型面向对象系统来看。
对于第一个方法,playSolo的时候我们要求的类型是能够演奏单音的。我们可以写出来

1interface SoloInstrument {
2  SoloInstrument playNote(Note note);   
3}

对于第二个方法,playChord的时候我们要求的类型是能够演奏和弦的,我们可以写出来

1interface ChordInstrument {
2  ChordInstrument playChord(Chord note);   
3}

而对于第三个方法,playBWV996的时候我们要求既能演奏和弦也能演奏单音,这个时候出现一个问题,我们怎么处理Instrument的继承关系?一个能演奏和弦的乐器是否可以演奏单音(答案是一般而言是的,但是也不排除有一些不是这样的)?还是我们简单的写:

1interface SoloAndChordInstrument extends SoloInstrument, ChordInstrument{
2}

或者

1interface BWV996Playable {
2
3  BWV996Playable playNote(Note note); 
4  BWV996Playable playChord(Chord note);  
5}

对于动态类型简单的隐性类型约定,显示的类型系统带来的一个副作用就是我们必须处理类型之间的关系。注意这里是类型之间的关系,而不是对象之间的关系。老庄同志批了很多篇的面向对象的抽象,面向对象的类型系统以及面向对象的本体论,其实都在是在类型关系上折腾,而不是在对象关系上折腾。而事实上面向对象的类型系统并非必然就是静态类型系统,而我们的类之间的关系不一定就和类型的关系相一致。就像上例所示,在Smalltalk里,Lute,Guitar和Bass之间没有任何的继承关系,但是对于person的3个消息而言,它们却是有类型的。

因此老庄所批的,是对象类型系统的抽象能力,而非面向对象的抽象能力。正如他在类型系统里所给的例子,那张他认为很失败的面向对象的图,其实可以完全不依赖继承来实现,而对这个类型系统的消费者而言,他们能够以一定的类型的观点,来处理这个系统的对象。

而老庄最后一个结论:

我的结论是:“一个类型,是由其本质决定了所能表现出的可操作性,而不是有其所能接受的操作决定了其本质。然而,OO正好把这个问题搞反了!”

我的看法是,这句话根本就是诡辩,前面半句的主语是“一个类型”,后面半句的主语是"OO"...

虽然前半句是对的,但是换一样说法可能更好:"所能接受的操作反映了其本质",面向对象本身就没有说我要做一个本质抽象,这一点在Smalltalk的类型判断操作上的可能是一个佐证,Smalltalk用isKindOf来判断继承关系,我们来玩一个文字游戏,改成俚语就是kinda,也就是"有一点,有几分"的意思,而不是说,“就是”,或者“从分类学上可证明之类的含义”。我再举一个龌龊的例子。

vincent ballon: AirBallon new.
vincent ballon: Condom 
new.

气球和保险套,对于ballon这个方法而言是一个类型,都是"有几分"可以吹起来。但是我怎么定义一个精确的本质?Ballonable?还是MakeFromLatexAndVeryThin?或者简单说FlexableAndThin?

在继承这一点上,我想老庄引文中:Elminster的话是从事物的特征与属性归纳出它的“类型”。恰恰对于静态类型面向对象系统是可行的。如我前文所述,我把一个object和所有sender的约定(也就是interface),继承在一起,恰恰就是一个颇为恰当的类型定义。

而对于动态类型系统里的面向对象语言,继承的也有类型继承的含义,但是并不是唯一的途径。用一句我们常说的话,在静态类型系统里,类型和类是紧耦合的,动态类型系统中他们的耦合比较松。

从此而观,所有对于面向对象的哲学考虑以及本体的思考,对于动态面向对象系统已经不是那么迫切了。而把对象类型系统的不足归咎于面向对象的不足,也似乎论据不足。