開篇之前先說明一下,我和老莊有著不錯的私交,他最初寫喪鐘系列的時候,我是忠實的擁躉之一,莊兄見我尚有寸尺所長,還在喪鐘系列里引用了我的幾個觀點。然而最近一段時間里,我作了這樣幾件事情:

1.重新學(xué)習(xí)了lambda演算。我的數(shù)學(xué)基礎(chǔ)不及taowen等,費了一些時日仍不完全了然,目前大抵能對著R5RS后面的語義定義對一些簡單的scheme程序進行l(wèi)ambda演算。
2.反復(fù)研究了幾遍SICP。同時看了錄像和書(感謝曹老師下的Video),自信對前二章半有些許認識書中未及,后二章半粗知大覽尚不能發(fā)前人所未發(fā)之言。
3.學(xué)習(xí)了Smalltalk和Ruby,Duck Typing以及其他一些有關(guān)類型系統(tǒng)的東西。
4.回顧了一下面向?qū)ο笳Z言的一些發(fā)展史,以史為鑒粗知一些興替。
5.經(jīng)日和taowen,老莊討論語言、設(shè)計等等之類,每為一辨必窮我所知爭發(fā)一言,所以知識可以迅速雜糅:D

經(jīng)過種種之后,發(fā)現(xiàn)當初所謂鳴OO之喪鐘,實在是言過其實。
--------------------------------------------------------------------------------------------------------------------------------------------------------------
第0 關(guān)于"面向?qū)ο?

既然要為人家敲喪鐘,先要找對了苦主,不然豈不是白忙活了一場。什么是面向?qū)ο螅窟@個問題太大,我們舉一個很常見的例子:

interface Instrument {
   
void playNote(Note note);
   
void playChord(Chord chord);
}


abstrat 
class Keyboard implement Instrument {
  ..
}


class ClassicGuitar implements Instrument {
   .
}


class Piano extends Keyboard {
  ..
}


in person 
class

void playSolo(Instrument instrument) {
   instrument.playNote(AbsoluteNote.C1).playNote(AbsoluteNote.C2);
   
}


void playBackground(Instrument instrument) {
   instrument.playChord(Chord.C5).playChord(Chord.C9);
   
}



in some 
case

ClassicGuitar perez711
= .
Piano pianoHere 
= .

vincent.playSolo(stenzel);
may.playBackground(pianoHere);

etc.

這個例子自然很不充分,不過繼承啊,接口啊,多態(tài)啊,略舉大概,尚可以作為一個討論的例子。從這個例子里,我們發(fā)現(xiàn)有這樣一個事實,就是Person類有兩個方法

void playSolo(Instrument instrument);
void playBackground(Instrument instrument);

分別表示,需要一個類型為Instrument的對象instrument,然后對他進行相應(yīng)的操作,而且,從概念上來講,第一個方法,使演奏旋律的,也就是單音,而第二個方法是演奏和聲的。那么,如果我有一個類

class GlassBottle {

   
void playNote(Note note) {
      ..
   }

}

我們知道,玻璃瓶裝了水也是可以發(fā)出聲音,Mozart可是為玻璃瓶寫過曲子的,而這里我們卻不能

GlassBottle beerBottle = .
vincent.playSolo(beerBottle);

因為我們在構(gòu)造Person類的時候,我們的play方法是依賴一個類型為Instrument的Object,換而言之,我們這里所謂的Object-Oriented其實稱作Object-with-Type-Oriented的更合適,其實這類語言有一個專有名詞,叫做static type object oriented。那Object-Oriented是不是就一定是Static Type Object Oriented的呢?不然,比如在Ruby里,我們可以寫成:

class Person 
  def playSolo(thing)
     thing.playNote(Note.C1).playNot(Note.c2)
  end
  def playBackground(thing)
     thing.playChord(Chord.C5).playChord(Chord.C9)
  end
end

class Guitar
  def playNote(note)
    
  end
  def playChord(chord)
    
  end
end

class GlassBottle
  def playNote(note)
    
  end
end

然后就可以

perez711 = Guitar.new
vincent 
= Person.new
vincent.playSolo(perez711)

同樣也可以

vincent.playSolo(GlassBottle.new)

在這里,類型可以推演也可以留到runtime檢查,如果是推演的話,在playNote里,由于thing調(diào)用了playNote方法,所以傳進來的對象必須要刻意接受playNote這個消息,于是Guitar和GlassBottle都是可以通過,因此我即可以演奏我的Perez 711,也可以敲玻璃瓶。這種方式叫做dynamic type。

那么static type和dynamic type區(qū)別在那呢?static type認為,class就是type,type就是class; subclass就是subtyping,subtyping就是subclass(其實這句不嚴謹,在c++里可以使得subclass不是subtyping,但是java里就沒辦法了);而dynamic type則認為類型和class沒有關(guān)系,類型取決于一個對象能夠接受的消息。

那么哪個才是面向?qū)ο笾婷材兀窟z憾的說,static type并不是最初的面向?qū)ο蟆R詫ο笞鳛槟K化的單位,始自Simula 67。但是Simula 67并不是一個面向?qū)ο笙到y(tǒng),以目前的觀點來看,充其量是Object-based,第一個面向?qū)ο笳Z言當推Smalltalk,Smalltalk是dynamic type的。不過Smalltalk不太流行,第一個大面積流行的面向?qū)ο笳Z言是C++,C++是static type的,正如Lisp是第一個函數(shù)式編程語言,很多Lisp的特性被當作函數(shù)式語言的共有特性(比如表是最重要的數(shù)據(jù)結(jié)構(gòu),輕語法)一樣,所以很多人以為面向?qū)ο缶捅厝皇莝tatic type(比如老莊和我)。對于面向?qū)ο蟮恼`解,80%來自C++。也就是說,80%來自于static type的面向?qū)ο笙到y(tǒng)。將static type面向?qū)ο髿w咎于整個面向?qū)ο螅逸厡嵲谑谴蠖鵁o當言過其實。

后面我再一一將老莊所言之OO不當之處加以說明