談談我對面向對象編程的經驗看法

前端時間,我看到了網上的一些人對於面向對象的弊端的討論,裏面對於面向對象的各種爭論一場火熱,這引起了我的深思。畢竟我也有多年的編程經驗,在編程的過程中面向過程和麪向對象這兩個編程範式也是經常的使用,也算是有了一點心得。附上原討論入口討論帖。我在這裏並不像抨擊任何人的意見,只是闡述我的一種看法。

在編程中,我是非常推崇面向對象的編程思想的。在我看來,面向對象可以很輕鬆的分割問題域,通過面向對象的封裝,多態和繼承,可以很輕鬆的開發龐大複雜的程序。

一個人的思維邏輯能力是有限的,他不可能面面俱到的處理到上到用戶交互界面,下到磁盤存儲形式的所有技術細節,這樣在開發中就需要利用到封裝的思想,封裝的思想讓程序員更加關注邏輯調用而忽略底層實現,這樣開發起來更加順暢。思維上順暢,可維護性也大大提高。因爲一個數萬行程序的開發工作不是一個人能夠完成的工作量,這樣巨大的工作量之後更需要有人進行維護,面向過程的編程方式會在代碼上暴露更多的技術細節,這對於理解整個系統的架構思路非常不利。因爲看不懂,所以代碼維護起來很難,很容易出現問題。面向過程雖然也能夠封裝,但是這種封裝是人爲添加到邏輯層面的,對於特化的編程還湊合,但是面對普式的編程比較低效了,面向對象天生鼓勵人們對於任務進行分割和封裝,因此更加適合大型項目的開發和維護。

面向對象擁有一個非常大的有點,就是多態,多態的實現可以在程序運行的時候動態的調用對象的方法,實現一個函數調用不同對象,產生不同行爲的神奇效果。這對於問題抽象非常有益,比如利用面向對象的思想,將雞和鴨抽象化,他們都有吃食,喝水,奔跑的行爲,都屬於禽類。這是他們的特點,那麼我編寫一個針對禽類操作的方法,就可以把他們傳進去,這樣就可以在傳雞的時候,吃食的方法表現出”啄“,傳鴨的時候,吃食的行爲表現出”鏟“。因爲不同的程序模塊在面對同一個輸入的時候可以產生不同的輸出,可以在運行時刻通過傳入的不同模塊組件,動態的構建系統,這在模塊化和組件化的系統中非常有用。可以說,多態是人們遠離利用面向對象編程的重要理由。

面向對象的另一個重要特點就是繼承,繼承使得人們能夠站在巨人的肩膀上征戰世界,利用一個已經封裝好的類,能夠快速的編寫新代碼,利用現有類提供的接口不僅僅是減少代碼量的問題還有系統適配的問題。通過繼承的方式對組件功能進行拓展,配合着多態的使用,簡直是系統化的編程的一種大殺器。無論繼承設計的目的是什麼,在我看來它確實是一種威力巨大的武器。用來拓展系統的功能實在是太適合不過了。

然而爲什麼有些人對於面向對象的編程方式如此詬病呢?

我認爲是絕大多數人對於面向對象編程的誤解在成了面向對象編程的錯誤使用而帶來的一些問題,對這些人造成了困擾。


封裝的經驗:

首先來說,封裝是一個非常好的問題解決思路,是構建大型程序的好手段。封裝這個工具的出現時爲了封裝底層細節,使得程序員能夠更加關注頂層邏輯實現的。也就是說,我們需要通過封裝來達到降低程序員邏輯負擔的目的,例如一個模塊的初始化過程,我們不能將所有的技術細節暴漏給上層程序員,那麼與未封裝相比還有那些優勢呢?我們可以很簡單的告訴上層程序員,調用這個函數,所有的變量屬性環境都初始化好了。構造函數可以做這些事情,但是並不推薦,應該單獨設立一個init函數,這樣不僅不降低抽象能力,同時還保存着簡單的性質,能夠通過返回碼等方式報告初始化過程中出現的意外情況。使得上層調用方能夠返回信息處理各種意外。另外這裏提到了初始化不得不提一下,強烈推薦一個類只有一個初始化函數,一個默認構造函數,這樣編程的附帶比較小。即使面對必須多個構造函數並存的情況,那麼一定要保證通過這幾個用於初始化的函數,初始化出來的數據結構是一直的,達到統一化的目的,不讓通過不同的構造函數初始化出來的變量具有不同的性質,這就可能造成你的程序陷入bug無限,完全不可維護的境地。

類對象中只暴露出方法,屏蔽屬性和字段,這樣的優勢很明顯。除非組件極其簡單,否則,每個類都可能在後期維護的時候,進行修改,程序架構之初如果沒有預料到這種情形,簡單粗暴的暴漏出字段和屬性,在後期維護的時候將會毀滅你的程序。

在對象方法設計之初的時候儘量將方法與類本身解耦,也就是,最好在設計之初的時候,就儘量減少直接調用類的屬性,而是通過函數的方法參數將類屬性傳進去。我們在編程的時候,需要的是封裝,但是封裝同樣存在一些問題,那就是,界限不好確定的問題,當代碼一多,程序就開始晦澀難懂,即使不斷的重構,還是收效甚微。這對於排故來說有些困難。對於這種問題,我也沒有非常好的辦法。現在我基本採用的封裝是對外部封裝細節,不需要對外面暴漏的細節全部封裝起來,也就是public方法對外封裝一切,而private方法則暴漏一切,所有的方法,能夠使用局部變量的絕不使用全局變量,能夠使用成員的絕不使用靜態變量,能夠從接口傳進來的絕不使用內部隱式調用。從類的結構上來看,類是一個多入口的小程序,每個類的public方法都是一個入口點,設計程序的時候一般情況下,將所有的保存狀態的變量交給入口程序掌管,接收調用的函數和方法,只使用臨時變量。這樣邏輯更加清晰,調試起來更加容易。


多態的使用:

有些人認爲多態是一個解決不斷變化的需求的神兵利器,認爲通過相同的調用方式產生不同的輸出結果,這樣就能應付所有的產品需求了,然而大多數這樣認爲的人都折戟沉沙了。爲什麼呢?根據我多年的編程經驗來看,我認爲這是兩種不同思路強行捏合在一起造成的惡果。是這些人錯誤的使用了多態的結果。沒有任何框架能夠適合一切情況的,產品經理提出來的功能能夠被完全歸類爲同一類操作麼?既然不能用同一種思路來操作,那麼使用多態還有什麼意義呢?

從軟件運行的步驟上來看。多態的使用是爲了利用同一種方式統一的調用同一類組件達成不同目的,它成立的前提在於你必須預料到所有可能的情況,構建出一個完整的最小系統接口,新的系統組件通過這個實現這個系統接口,能夠達到既擴充了系統的既有功能又實現了對既有系統零更改的目的。這種情況非常適合插件化開發,而且是在已經定型的系統上進行插件開發,當然前提是你爲了這種插件機制提供了內部的兼容機制。如果原來的系統架構設計垃圾,這種方式是無法完善整個系統的設計的,不能因爲今天覺得以前的設計應該增加一個接口,就增加一個接口,卻又不想改老程序,這樣誰來調用你呢?有一些變態的架構師爲了實現這個目的竟然連Java的反射都用上了。利用反射運行時關聯等手段,成功的達成了這一個醜陋的目的之後,程序的邏輯代碼就成功的從程序下放到了配置文件和數據庫之中,管理困難,運行緩慢,bug奇多。備受其害。


繼承的使用經驗:

我認爲繼承是一個好東西,然而無論繼承多麼好都不應該濫用。擁有繼承和多態相配合,程序很容易出彩。

繼承不能濫用就表現在,繼承應該有個原則,如果不是需要用到超類的所有功能就不要繼承超類,因爲這會帶入亂七八糟的功能,給編程造成負擔,引入bug。

繼承可以將同一種操作提取出來,這樣就可以少編寫一些代碼,這不僅僅省了一些力氣,同樣能夠提高系統的可靠性,免得在基礎功能上引入新bug。

繼承最適合實現一些基礎的操作,比如在窗口程序的開發中利用基類實現消息循環,空間之間的關聯等操作,強烈反對胡亂繼承,引入一堆不需要的方法和接口,最噁心的就是Java的swing類庫,這個實現的確實挺經典的。但也是挺骯髒的,其中包含了巨量的無用接口,這些從根對象層層繼承下來的用不到的接口亂七八糟的放在一起,糟糕透了。

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