在現代,物件導向雖然仍是主流,函數式慢慢得到關注。物件導向並不適合所有的程式邏輯,但在像是Java的物件導向的程式語言中,幾乎所有值都被當作物件,因此在一些情境下Java寫起來會非常冗余。物件導向流行的原因大概是因為它的思考方式比較符合我們對於世界的認知,但邏輯推理與解決問題的方式卻不一定符合我們的想像,若是硬要用物件導向的方式設計程式,就會使得程式架構變得奇形怪狀。或許是因為程序猿累積了足夠多的失敗經驗,又或是人類對於世界的認知改變了,物件的思考方式在最近漸漸的被放棄。然而究竟什麼是「物件」?
「物件」這個詞似乎一直沒有明確的定義,只是一個抽象的概念。在這裡我想討論的是物件(class/object)與結構(struct)差在哪裡。c++中class與struct基本上是沒有任何差異的,但有些人會特別選用其中一種定義,而我想探究其背後的想法。
對於c#來說,class與struct最大的差別就是ref/value type:物件總是以參考的方式傳遞。Java也有類似的特性,只是Java只有一些原始類別是value type。只有在這種刻意消除指標概念的GC程式語言才有這種差別,很多高階程式語言如Python, JavaScript 等都有相同的特性。這種消除指標的做法跟函數式非常像,由於參考透明的特性,大多數函數式程式語言都沒有記憶體的概念。然而這類物件導向的程式語言卻非常依賴資料的修改,因此就算沒有指標的概念,有時也必須要判斷變數是否是同一個「東西」,而這種身份的判斷就形成了物件的概念。然而這只是把指標的概念簡化而已,雖然物件需要用指標,但並不是所有指標都是物件。然而在這類的程式語言就連陣列都被當作物件(我認為它不算物件,如果連一串數字都必須當作物件處理,就太過冗余了)。因此我不認為這是判斷是否應該用物件包裝的依據。
現代的物件導向主要有四個特性:封裝、繼承、多型、抽象。這些特性主要圍繞著一個設計理念 – 把程式分解為資料與相關的操作並封裝起來,而這個概念就形成了「物件」。然而一些結構如陣列和智慧指標也帶有方法,事實上方法就只是以物件為主要參數的函式而已。因此我認為有沒有方法並不是區分物件的指標。而「封裝」不一定要和資料綁在一起,只是對於屬於物件的資料來說這是必要的。但我認為提供物件層級的封裝會使得物件導向的模組化變的畸形複雜,最好的方法應該是讓封裝更容易控制,而非跟物件綁在一起。這種微妙的差異使得物件導向漸漸變成另一種怪物。
smalltalk 作為最早的物件導向程式語言,對於物件導向有另一種詮釋:物件是一種「對象」,你可以藉由方法跟他「溝通」以完成某些目的。這個解釋強調的是物件的「獨立性」,他具有某些隱藏的狀態,我們只能藉由溝通改變他的狀態。現代的物件導向延伸自這種詮釋,但重點卻變為封裝資料與方法。這種具有隱藏狀態的特性非常像閉包closure,事實上物件的確可以藉由閉包建構。我認為「閉包」最適合描述物件的概念,從這裡可以看到它與現代物件導向的差異。閉包的封閉性引導出了現代物件導向的封裝,但閉包並沒有複雜的存取控制,而這也不是它的主要目的。閉包本身也沒有繼承機制,而是藉由互相合作完成更複雜的功能,我認為這種分而治之的方法優於繼承。定義物件的目的應該是捕獲複雜的運算狀態,而不是為了抽象化某種行為。現代的物件導向依賴於繼承以達成抽象化,但相較於繼承,介面和泛型更適合描述行為的抽象化。物件導向一路走歪到現在,人們才發現做錯了,現在學習物件導向都會建議初學者少用繼承多用介面。我想最好的建議是不要使用現代的物件導向。