第1 关于"接口"

关于接口的问题老庄说对了,这个东西并不属于面向对象的概念,而且在动态类型面向对象语言(比如Ruby, Smalltalk里),根本没有这个东西。这是一个纯粹的静态类型面向对象语言的特性,或者直接说,接口就是一个纯类型(Type)。还是上次的例子:

 1interface Instrument {
 2   void playNote(Note note);
 3   void playChord(Chord chord);
 4}

 5
 6in Person Class
 7
 8void playSolo(Instrument instrument) {
 9 
10}

在我接触Smalltalk的最开始的一段时间里,这种地方是让我最难受,我已经习惯了用类型辅助我的思维,但是我发现我在Smalltalk里做不到,虽然我可以写出

Instrument>>playNote:note
  
^self subclassResponsibility.

Instrument
>>playChord:chord
  
^self subclassResponsibility.

但是他却不是一个接口,我可以很用

instrument = Instrument new.
instrument playNote: Note C.

来构造一个Instrument之后在它之上调用playNote方法,然而我会得到一个messageNotUnderstand的错误,Smalltalk和Ruby里没有Abstract的概念。也就是说abstract method,abstract class以及interface,都不是面向对象的概念(或者严格一些说,都不是面向对象的必须的概念),而是面向对象类型系统的概念。那么在Smalltalk里我们会怎么做呢?

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;

对于playSoloOnInstrument:instrument,我们对于instrument的类型是有要求的,就是它必须能够接受playNote这个消息。当然这个类型需要是隐性,我也可以对等的写出静态面向对象的代码把这个隐性的类型显示的表示出来:

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

同样对于第二个方法我们也可以写出来:

1interface Instrument {
2   Instrument playChord(Note note);  
3}

如果我们需要多于一个的消息也是一样的,比如

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

同样这个时候隐性的类型需要就是

1interface Instrument {
2   Instrument playNote(Note note);  
3   Instrument playChord(Note note);  
4}

那么接口是什么呢?我给出一个不确切的说法,interface是一个消息的发送者(sender)和一个消息的接受者(reciver)间的一种类型的约定,也就是说在我看来interface的用处主要在细粒度的显式类型约定。我有一个同事,每次写代码都为一个Test Case所要测试的对象定义一个interface,每个interface都只有2-3个方法(lx同学夸你呢:D),这是很得interface之三味的用法。这种的做法对于在静态的面向对象系统的好处我们在继承里再论述。

至于老庄所说的接口是多继承的一种代替品,这只不过是世俗的看法,在静态类型的面向对象里,继承至少有2个语义:

1.实现继承,这个在Smalltalk,Ruby,C++里有,而在java里没有,C++里是通过private extends来实现的

1class SubClassA: private ImplementationParent {
2}

这也是C++里为数不多的subclass不是subtype的例子。

2.类型继承,这个在C++和java里有,而在smalltalk,ruby有却不明显。

类型继承的极致就是C++里的纯虚父类

1abstract class Parent {
2
3  public asbtract void method1() = 0;
4  public asbtract void method2() = 0;
5}

也就是java里的interface

1interface Parent {
2   void method1();
3   void method2();
4}

因此,也就明了了,所谓“面向接口编程”是以类型作为约定的编程。我觉得这点大家一定不陌生,面向接口的编程里interface都很小而且约定明确。但是要说明一点的是,这个东西和"面向抽象而不要面向具体编程"其实还不一样,所以这个东西也就仅仅能算是静态类型面向对象的一个惯用法,还到不了原则这么高。