別給計算機科學學生教面向對象編程

面向對象理念是編程時入手問題的一種人類理解意義上的“具象”,在性能關鍵的領域(科學計算、圖形渲染、大數據處理等)這種“具象”所建立起來的計算模型十有八九不是計算效率的最優解。學校裏填鴨“封裝”“繼承”“多態”是對於計算過程的黑盒化,說嚴重點是一種“思維束縛性”的誤導,因此計算機“科學”系的學生和碼農應該透過現象熟諳本質,不被流行概念矇蔽自己本該樹立的計算三觀。(如果是非專業人士,想學編程玩,或者帶來職場機動性的,可以關掉瀏覽器了,不用浪費時間看本文。面向對象對編程技能的普及功不可沒,放開學吧。)

 

面向數據編程的理念(Data-Orientated Programming)在近年來被提及,之所以說這個概念,很大程度上就是去dis面向對象這個概念。計算的本質是輸入和輸出,改變狀態、讀寫數據本來就是程序的目的。寫的代碼最終存在硬盤上,活在內存裏,所謂的數據是數據,代碼也是一種數據。代碼會被某些計算單元處理,或者被某些虛擬機所處理,而虛擬機的的代碼也是代碼最終被計算單元處理。

 

下面說說面向對象的一些誤導:

實施模型(implementation model)和人類心理模型(mental model)的差異。在虛擬世界的建模中,一把椅子,可能是靜態的環境的一部分,可能是動態的可以交互(人可以坐,可以搬起),也可以是能夠被物理模擬所打碎、掰彎、點燃燒蝕。上述三種類型的椅子,在實施和處理上共性很少。即使能夠抽象出一些共同的屬性,寫成繼承的形式是對計算過程的巨大羈絆。計算機的每一級緩存都有有限的cache line,如果能夠把要處理的數據集中起來,按一定格式排列或者壓縮起來,放在臨近位置,那最終在硬件上的運行效率和對此”不管不問”的面向對象模型會有天壤之別。Array of structs 和struct of arrays的例子不勝枚舉。前者雖然歸類在一個struct很多屬性,但是對於alignment,何時何地處理何種數據都是一種“忽視”,在實操時緩存miss掉的概率很高。而如果有意的去排列所要操作的數據,根據數據的依賴關係構建計算管線,那最終的運行起來是處理器非常舒服的flow。

 

計算機科學的學生要學編譯原理。編譯的前端需要對文本進行分析和轉譯。當我們在說”繼承“的時候,好像默認了一個包圍在花括號裏叫class的東西,衍生出了新的類型。可是關於其成員數據如何存取在內存裏,成了一個不管不問的黑盒。加了prviate的變量,子類看不到就不存在了嗎?能夠通過offset取到嗎?子類如果有個一模一樣名字的變量會發生什麼?變量的名字對於編譯器意味着什麼?Virtual table怎麼找到你override的函數的?找的過程消耗如何的計算資源?一個class有一百個函數,一個class有三個函數,誰佔內存多還是壓根沒關係?當你在原地寫lambda的時候,到底發生了什麼?所有的東西都是一個object,會淡化primitive類型的存在感,會忽視內存alignment的意義。會淡化傳reference的意義(如果reference永遠是以copy的形式當value被傳),沒有pointer會忽視對地址概念的理解和思考,pointer和reference被編譯器翻譯成的指令有何不同?面向對象概念所建立起的對程序的感官體驗,是對編譯原理學習的巨大幹擾。編譯原理所要解決的問題是本質的人機交互過程。而一些帶花活語法的編程語言,複雜的詞法語法語義分析把使用者和計算機成功的割裂開來,在一定意義上,它讓編程更簡單(C#/.NET, Java)。同時這些花活帶來的誤區需要很大的後天努力才能掰回來,這是一個打破和重建三觀的過程。

 

計算機科學是科學,科學是抽象的,抽象是痛苦的。但痛苦的意義在於成就卓越。可讀性差的程序,碼農多看幾遍看文檔可以理解。運行慢的程序,計算機跑了十遍也不會自發讓它更快。現在技術棧在不斷往上加,層與層之間的抽象黑盒化讓應用軟件可以遍地開花。然而跑得快,跑得多,不代表能跑得遠。科學會在文明毀滅後幾乎一樣的被重新發現,抽象的概念會抽象地被另一個領域在不知道多久之後被重新拾起。長遠考慮,面向對象的道理,更像人類去說服自己接近”計算思維“時,給自己編造的一則聖經故事,老少咸宜。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章