若是把我已經粗略接觸到的編程範式分類的話,大概能夠分爲面向過程、面向對象。固然,還有僅僅據說過的函數式編程和不少沒據說的。linux
- 命令式編程語言就至關於馮諾依曼語言,也基本等同於一般所說的面向過程編程。
- 全部把修改變量的值看成最基本計算方式的語言均可以稱做馮諾依曼語言,包括咱們熟悉的C,Fortran等待。這類語言是創建馮諾依曼體系結構之上的。因爲馮諾依曼體系結構,這類語言的核心有:模擬存儲單元的變量,基於傳輸操做的賦值語句,以及迭代形式的循環運算。所以從某種程序上,這類語言是基於計算機的另外一種數學模型(圖靈機)的,實現了對計算機硬件結構的抽象。函數式語言的基礎是具備值的表達式,而馮諾依曼語言的基礎是語句(特別是賦值),他們經過修改存儲器裏面的值而產生反作用(side effect)的方法去影響後續計算。
簡而言之,馮諾依曼語言核心:模擬存儲單元的變量,基於傳輸操做的賦值語句,以及迭代形式的循環運算。是對圖靈機(數學模型)的實現。把修改變量的值看成最基本計算方式。
- 對於如今流行的面向對象編程,最開始的目的是「模擬」現實中的結構;其次是__下降GUI程序設計的門檻__,但這更可能是一個實現問題,而不是設計問題。
- 面向對象始於__模擬__應用,後來被視爲面向過程編程沒法__向巨型項目擴展__絕症的解藥。再之後被『發揮』到極致,無論適不適合都要用面向對象的方式去解決,應了那句老話『錘子眼裏全是釘子』。
- 這個問題的根本在於 OOP 是基於狀態的。每一個對象都維護着本身的狀態,暴露給外界的是一些能夠改變對象狀態的方法。一個對象的狀態裏能夠有對其餘對象的引用,一個對象的方法也能夠調用其餘對象的方法來改變其餘對象的狀態,因此這些狀態仍是關聯的。
- 面向對象的核心是封裝狀態和相應的過程。一般面向對象是經過改變內部狀態實現最終目的。調用對象過程的主要目的是產生改變其內部狀態這個反作用(side effect)。這樣封裝的初衷是避免多個主體訪問、修改同一狀態形成混亂。在很多場合這樣的封裝確實也達到了目的,因此面向對象的方式才這麼普及。
封裝:封裝的意義,在於明確標識出容許外部使用的全部成員函數和數據項,或者叫接口。
有了封裝,就能夠明確區分__內外__,使得類實現者能夠修改封裝內的東西而不影響外部調用者;而外部調用者也能夠知道本身不能夠碰哪裏。這就提供一個良好的合做基礎——或者說,只要接口這個基礎約定不變,則代碼改變不足爲慮。
繼承+多態:繼承和多態必須一塊兒說。一旦割裂,就說明理解上已經誤入歧途了。
先說繼承:繼承同時具備兩種含義:
其一是繼承基類的方法,並作出本身的擴展——號稱解決了代碼重用問題;
其二是聲明某個子類兼容於某基類(或者說,接口上徹底兼容於基類),外部調用者可無需關注其差異(內部機制會自動把請求派發[dispatch]到合適的邏輯)。
再說多態:基於對象所屬類的不一樣,外部對同一個方法的調用,實際執行的邏輯不一樣。
實踐中,繼承的第一種含義(實現繼承)意義並不很大,甚至經常是有害的。由於它使得子類與基類出現強耦合。
繼承的第二種含義很是重要。它又叫「接口繼承」。
接口繼承實質上是要求「作出一個良好的抽象,這個抽象規定了一個兼容接口,使得外部調用者無需關心具體細節,可一視同仁的處理實現了特定接口的全部對象」——這在程序設計上,叫作__歸一化__。
歸一化使得外部使用者能夠不加區分的處理全部接口兼容的對象集合——就好象linux的泛文件概念同樣,全部東西均可以當文件處理,沒必要關心它是內存、磁盤、網絡仍是屏幕(固然,若是你須要,固然也能夠區分出「字符設備」和「塊設備」,而後作出針對性的設計:細緻到什麼程度,視需求而定)。
歸一化的實例:
a、一切對象均可以序列化/toString
b、一切UI對象都是個window,均可以響應窗口事件。編程——必須注意,是一切(符合xx條件的)對象皆能夠作什麼,而不是「一切皆對象」。後者毫無心義。設計模式
顯然,歸一化能夠大大簡化使用者的處理邏輯:這和帶兵打仗是相似的,班長鬚要知道每一個戰士的姓名/性格/特長,不然就不知道該派誰去對付對面山坡上的狙擊手;而連長呢,只需知道本身手下哪一個班/排擅長什麼就好了,而後安排他們各自去守一段戰線;到了師長/軍長那裏,他更關注戰場形勢的轉變及預期……沒有這種層層簡化、而是必須直接指揮到每一個人的話,累死軍長都無法指揮哪怕只是一場形勢明朗的衝突——光一個個打完電話就能把他累成啞吧。網絡
軟件設計一樣。好比說,消息循環在派發消息時,只需知道全部UI對象都是個window,均可以響應窗口消息就足夠了;它不必知道每一個UI對象到底是什麼——該對象本身知道收到消息該怎麼作。編程語言
合理劃分功能層級、適時砍掉沒必要要的繁雜信息,一層層向上提供簡潔卻又完備的信息/接口,高層模塊纔不會被累死——KISS是最難也是最優的軟件設計方法,沒有之一。ide
——回答我,封裝是否是等於「把不想讓別人看到、之後可能修改的東西用private隱藏起來」?函數式編程
顯然不是。
若是功能得不到知足、或者不曾預料到真正發生的需求變動,那麼你怎麼把一個成員變量/函數放到private裏面的,未來就必須怎麼把它挪出來。函數你越瞎搞,越去搞某些華而不實的「靈活性」——好比某種設計模式——真正的需求來臨時,你要動的地方就越多。spa
真正的封裝是,通過深刻的思考,作出良好的抽象,給出「完整且最小」的接口,並使得內部細節能夠對外透明(注意:對外透明的意思是,外部調用者能夠順利的獲得本身想要的任何功能,徹底意識不到內部細節的存在;而不是外部調用者爲了完成某個功能、卻被礙手礙腳的private聲明弄得火冒三丈;最終只能經過怪異、複雜甚至奇葩的機制,才能更改他必須關注的細節——並且這種訪問每每被實現的如此複雜,以致於稍不注意就會釀成大禍)。設計
一個設計,只有達到了這個高度,才能真正作到所謂的「封裝性」,才能真正杜絕對內部細節的訪問。
接口繼承真正的好處是什麼?是用了繼承就顯得比較高大上嗎?
顯然不是。
接口繼承沒有任何好處。它只是聲明某些對象在某些場景下,能夠用歸一化的方式處理而已。
換句話說,若是不存在「須要不加區分的處理相似的一系列對象」的場合,那麼繼承不過是在裝X罷了。
封裝可應付需求變動、歸一化可簡化(類的使用者的)設計:以上,就是面向對象最最基本的好處。
——其它一切,都不過是在這兩個基礎上的衍生而已。
相似的,我遇到過寫遊戲的卻去糾結「武器裝備該不應從遊戲角色繼承」的神人。你以爲呢?
事實上,遊戲界真正的抽象方法之一是:一切都是個有位置能感覺時間流逝的精靈;而某個「感覺到時間流逝顯示不一樣圖片的對象」,其實就是遊戲主角;而「當收到碰撞事件時,改變主角下一輪顯示的圖片組的」,就是遊戲邏輯。
看看它和「武器裝備該不應從遊戲角色繼承」能差多遠。想一想到得後來,以遊戲角色爲基類的方案會變成什麼樣子?爲何會這樣?
最具重量級的炸彈則是:正方形是否是一個矩形?它該不應從矩形繼承?若是能夠從矩形繼承,那麼什麼是正方形的長和寬?在這個設計裏,若是我修改了正方形的長,那麼這個正方形類還能不能叫正方形?它不該該天然轉換成長方形嗎?什麼語言能提供這種機制?
形成這顆炸彈的根本緣由是,面向對象中的「類」,和咱們平常語言乃至數學語言中的「類」根本就不是一碼事。
面向對象中的「類」,意思是「接口上兼容的一系列對象」,關注的只不過是接口的兼容性而已(可搜索 里氏代換);關鍵放在「可一視同仁的處理」上(學術上叫is-a)。
顯然,這個定義徹底是且只是爲了應付歸一化的須要。
這個定義常常和咱們平常對話中提到的類概念上重合;但,如前所述,根本上卻不折不扣是八杆子打不着的兩碼事。
就着生活經驗濫用「類」這個術語,甚至依靠這種粗淺認識去作設計,必然會致使出現各類各樣的誤差。這種設計實質上就是在胡說八道。