物件導向的本質是訊息:Alan Kay 所說的真正 OOP
學習物件導向時,我們總是先背下繼承、封裝、多型這三個詞。
然而,創造「物件導向程式設計」一詞的 Alan Kay,並沒有把這三者視為核心。
先說重點:Alan Kay 所說的真正 OOP,本質是物件之間往來的訊息,而不是物件本身。比起如何劃分類別,更重要的是物件彼此交換什麼。
讀完本文,你就會理解他為何甚至說自己「後悔使用物件導向這個名稱」,以及這個觀點如何改變我們的程式碼。
Alan Kay 為什麼後悔使用「OOP」這個名稱?
Alan Kay 在 1970 年代於 Xerox PARC 創造了 Smalltalk。「物件導向程式設計」這個術語也是由他提出的。
2003 年,一名開發者透過電子郵件問他「什麼是 OOP」,他如此回答:
「我後悔使用了『物件』這個詞。
因為這讓人們把注意力放在較不重要的概念上。真正偉大的想法是『訊息傳遞』。」
第一次看到這句話時,難免有些困惑。我們學到的物件導向,總是「如何設計出良好的物件」。
在 Kay 看來,真正重要的不是物件內部,而是物件透過交換訊息形成的關係與互動,那才是系統的本質。
他把程式比喻成生物細胞。每個細胞都嚴密隱藏內部,只透過化學訊號,也就是訊息來溝通。網際網路也是如此:無數電腦各自獨立運作,只交換訊息。
以訊息為中心的思考有何不同?
「呼叫方法」和「傳送訊息」看似相近,但觀點不同。
呼叫方法比較像「執行這個物件的這個函式」。傳送方必須在某種程度上了解接收方的內部。
相對地,訊息比較像「請處理這件事,方法由你決定」。傳送方只要結果,不必知道對方如何處理。
來看一個例子。以下程式碼將折扣計算以「訊息」委派給各會員等級物件。
protocol Member {
func discountedPrice(for price: Int) -> Int
}
struct Gold: Member {
func discountedPrice(for price: Int) -> Int { price * 80 / 100 }
}
struct Silver: Member {
func discountedPrice(for price: Int) -> Int { price * 90 / 100 }
}
// 傳送方完全不知道各等級的計算方式.
let members: [Member] = [Gold(), Silver()]
for m in members {
print(m.discountedPrice(for: 10000))
}
// 輸出: 8000
// 輸出: 9000
呼叫端程式碼中沒有 if 陳述式,也沒有等級名稱。
它只傳送「請計算折扣價格」這則訊息,實際計算由各物件負責。即使新增等級,也不必修改呼叫端。
這就是 Kay 所說的訊息傳遞之力。重點不是把物件切得很細,而是降低耦合的溝通方式。
那封裝與多型就不需要了嗎?
不,恰恰相反。
認真看待訊息傳遞後,封裝與多型會自然出現。
物件若只能透過訊息溝通,就必須隱藏內部狀態。這就是封裝。同一則訊息由不同物件做出不同反應,就是多型。
這些概念不是需要死背的規則,而是以訊息為中心設計時自然產生的結果。
問題在於順序。許多人從「先把類別好好劃分」開始。結果物件雖然大量增加,卻彼此看透內部,容易產生只有名稱像物件導向的程式碼。
Kay 的觀點把順序倒過來。先問:「這個物件應該回應哪些訊息?」
何時該採用這個觀點,何時又該適可而止?
以訊息為中心的設計不一定永遠正確,重要的是依情境使用。
| 情境 | 判斷 |
|---|---|
| 需求經常變動的領域邏輯 | 採用以訊息為中心的委派結構較有利 |
| 合作物件眾多的複雜流程 | 對降低耦合度非常有效 |
| 簡單的資料轉換・計算指令碼 | 不必勉強包成物件,使用函式即可 |
| 效能極為重要的區段 | 過度抽象反而會成為負擔 |
總結如下。
- 越是需要協作與變更的地方,訊息觀點越能發揮作用。
- 簡單且固定的邏輯,保持輕量會更好。
- 請記住,目的不是「劃分物件」,而是「設計溝通方式」。
面試時可以這樣回答
Q. Alan Kay 所說的物件導向本質是什麼?
不是物件本身,而是物件之間往來的訊息。Kay 將物件比喻成能獨立溝通的細胞,認為訊息傳遞比繼承與封裝更大的想法。
Q. 以訊息為中心的設計在實務上有什麼好處?
呼叫端不必知道對方的內部實作,因此耦合度會降低。如此一來,即使新增型別,也不必修改呼叫端,結構更能適應變更。
如果覺得物件導向很難,請在畫類別圖前先想想:「這些物件彼此在交換什麼話?」
只改變一個觀點,就能感受到程式碼變得更加穩固。祝你今天也能做好設計!

