面向對象與函數式編程做對了什麼?
函數式編程,其核心是函數與調用。函數是待解問題里的概念,調用是解決問題過程中的推導。
面向對象做對了消息派發,函數式編程做對了執行控制。
對於面向對象編程,消息觸發接收者的動作,消息的派發由編譯器或運行時管理。函數調用是消息派發的一種實現手段, 執行效率高,通常在編譯時已經決定消息由接收者的哪段代碼響應。更為靈活的實現里,編譯器只生成消息分發調用,運行庫裡面的消息分發機制在執行時查表決定到底執行哪一段代碼。
由於消息分發機制的存在,架構演進時將對象替換或分開布署是非常方便的。如果一開始就採用靈活的分發機制,那麼替換消息目標對象或者將對象分布到不同進程、節點上可以變成對使用者完全透明,可能只需要做一些配置修改就行,當然這樣在不需要這種靈活性時就有一點性能上的代價。
函數式編程環境里的函數是自包含的,不允許狀態依賴,執行順序也不是一道順序的指令流,很自然的需要編譯器或運行時提供任務調度機制,這樣高階函數、lambda表達式之類的執行片段才能當做數據傳遞並在正確的時刻、環境下執行。
由於執行控制機制的存在,函數式編寫非同步處理和並發響應是非常便利的。
在對世界進行抽象方面,咋一看,面向對象認為一切都是對象,狀態與行為並列;函數式認為一切都是函數,狀態附著於行為;而對象之間只應該通過消息互動,應用開閉原則(OCP),其實狀態也是附著於行為的;因此可以說FP在抽象手段方面更直白,而OOP則有更多的犯錯機會。另一方面,嚴格說來,函數式編程里的狀態是以函數的參數和返回值來表達的,因此變化過程是清晰、可預測的,但用來表達問題未免繁瑣,實踐里還是拿閉包將狀態與行為封到一起方便。以及在馮氏架構的機器上還得盡量搞成尾調用,不然容易爆棧,雖然坑在機器架構不配合,不過這年頭沒得選嘛。
順便一提,類、繼承、多態都不是面向對象編程必備的部分,僅僅是為了方便代碼重用而搞出來的。實際上,Alan Kay 在正式提出 Object-Oreinted 之前,Simula I/67 已經存在並實現了繼承,但是 Alan Kay 認為繼承並不適合做為OOP基本組成部分,這要感謝他的生物學背景(本科專業數學與分子生物學)讓他更希望用細胞計算機的方式看待程序。
寫到這裡,不禁對 Scala 產生了一點興趣……但是掃了一眼語法,算了,還是 JavaScript 比較爽。
找插圖的時候發現,Oreilly 居然出了本以這個為主題的書,還是免費的:Object-Oriented vs. Functional Programming - OReilly Media。插圖就是書的封面了,算是幫它做個廣告吧。
推薦閱讀:
※提醒:別睡了,起來學習吧(cs61a,cs61b Sp18開課了)
※暫時不會再寫關於 Kotlin 的東西了
※PHP 7 新特性(完結篇)
※從 0 開始學習 GitHub 系列之【GitHub 常見的幾種操作】