最近在評審團隊的代碼,發現還存在一些問題,比較重要的問題是面向資料庫程式設計和用面向對象的語言寫着面向過程的代碼。為此對面向對象程式設計思想進行了一個總結,以便盡快建立面向對象程式設計思想。
在此形成文字稿,分享出來大家一起探讨。
言歸正傳。
整個程式設計語言主要經曆了從面向過程的語言到面向對象程式設計語言的過程(其他程式設計思想不在本文讨論範圍之内,如:面向函數程式設計)。
圖:程式設計語言的發展分析
面向過程程式設計語言主要代表是彙編、C語言等。
彙編語言對開發者要求高,開發效率低,除非某些必要場景,如:BIOS,現在很少有人用其開發。為提高開發效率,1972年,美國貝爾實驗室的丹尼斯·裡奇發明了C語言,C語言的開發效率大幅提高,随着面向過程程式設計的指導思想——“資料結構+算法=程式”深入人心,很多程式員在尼古拉斯·沃斯畫的圈圈裡打轉,不能自拔。本人認為面向過程程式設計思想是賬本思維,引導人重點關注這本賬,即資料結構,其他的操作,即算法,都是去修改操作這本賬,賬本和賬本操作的方法兩者的關系在人看來割裂,關系弱,當後期需求變更時,不容易掌握,控制不好需求的改動範圍,傳染不好控制,導緻後期演進維護成本高。
面向對象程式設計語言主要代表是C++、Java、Go等。
C++語言是集大成者,文法特性豐富,開發效率高,但由于文法特性過于豐富和靈活,導緻難掌握,團隊協作時,容易出問題,如,多繼承會是子類有多個父類,經過多重繼承,子類的父類有相同方法等,後期維護會找不到北,還有容易出指針,為此,如果選擇用C++開發,則會限制文法範圍,在團隊限定的文法規範内研發,總體來說,對底層性能要求高的。
為解決C++的這種弊端,适時地推出了Java語言,Java語言擁有很多特性,如:跨平台、單繼承、反射、抛棄指針、GC等。
1、跨平台解決一次編譯多處使用,提高程式的可維護性。
2、單繼承解決縱向依賴混亂問題。
3、反射本人認為是Java最大的特性,反射的根源在于Java相較于C、C++保留了Metadata資訊,是以,Java可以根據對象的位元組碼動态構造對象,而C、C++在編譯時未保留這些資訊,是以,無法從二進制中動态生成對象,這也是Strus、Spring、Hibernate等系列元件存在的基礎,正是由于有了這些豐富的Java中間件,促進了Java生态的發展,在web應用開發領域一枝獨秀。
4、GC則把程式員從繁雜的記憶體申請與釋放的管理中解放出來,同時GC使得記憶體的使用率更高,降低出現記憶體碎片化的機率,但GC管理記憶體時會帶來性能損失,具體可以參考GC的相關機制材料,本文不做深入探讨。
Java語言由于支援跨平台的特性,在實作上需要先編譯成位元組碼,在執行時再由Java虛拟機解釋執行,雖然Java虛拟機采用各種機制進行優化,效果也較明顯,但與C/C++從語言效能相比,性能總體還是存在一些差距。
Go語言應運而生,Go的目标是21世紀的C語言,Go是面向對象的語言,文法接近C語言,但對于變量的聲明有所不同。Go支援垃圾回收功能,取消了繼承,使得對象的縱向關系弱化,隻能使用對象的橫向聚合與組合關系,從文法機制上降低亂用的機率。
從程式設計語言的發展趨勢來看,程式設計語言對開發者的要求越來越低,語言的開發效率越來越高,但對開發者的設計能力卻要求越來越高。
實際上,大部分人都是用着面向對象的語言寫着面向過程的代碼,導緻開發效率低、産品維護和演進困難。造成這些問題的根源在于對面向對象精髓的了解有所欠缺,本人認為主要展現在兩個方面:
1、思維定式問題:還是以記賬的方式在思考問題,設計時首先考慮資料庫怎麼設計,資料要怎麼存,有了這個賬本後,再在賬上進行增删查改,而不是采用先物化成對象,再抽象成類,然後通過聚合群組合進行對象組裝,程式設計思想的認識還是停留在面向過程的程式設計思維中。
2、思維誤區問題:在文法學習時,好像都懂,但一旦與具體業務結合時,沒有有效的分析手段——如何物化抽取對象,對象的功能劃分、對象間該如何組合群組裝等等一系列問題。最終的表現還是用面向對象語言寫着面向過程代碼。
即,還是“資料結構+算法=程式”的思維方式,基于我的了解,我嘗試着對面向對象程式設計給出我的了解“對象+組合/聚合=程式”,供大家參考。
本人認為,目前面向對象程式設計模型的最佳實踐方法是領域驅動開發,領域模組化的核心原則是“聚合根+充血模型”,通過領域模組化,物化抽取對象,按充血模型原則把相關的功能劃分到正确對象中,然後根據場景關系組合/聚合對象間的關系,形成聚合根。
以一個簡單報賬場景為例,簡要說明領域驅動開發的領域模組化的過程:
1、第一步:抽取報賬人、報賬單、差旅票、住宿票證等為一個個對象,即物化過程,随之把總金額計算、行程金額計算、酒店金額計算等相關的功能按充血模型的原則放到合适的對象中,讓各對象各司其職,類似于汽車工業的各種零件加工。
2、第二步:建立對象間的聚合/組合關系,如:報賬人組合/聚合關聯報賬單,報賬單組合/聚合挂鍊住宿票證和差旅票證等,形成聚合根的方式,即對象組合/聚合形成聚合根的過程,同時把對象間關聯的相關功能,按充血模型原則放到合适的對象中。借助面向對象的語言組合/聚合的特性進行關聯關系的組織,類似于汽車工業的汽車組裝過程。
在組合/聚合過程中,有兩個需要特别注意。第一,對象間盡量不要形成回環。第二,對象的通路,需遵循隻能從聚合根進行通路,即,形成單向的聚合和通路關系的秩序,使得對象間關聯關系和通路原則簡單,便于分工協同、對象組裝以及産品的的維護和演進,提高生産效率和降低維護演進成本,類似于工業大生産中的标準化分工、工序化協作以及産品售後的維護和産品的演進等。
掌握了面向對象程式設計思想的精髓,用面向過程的語言同樣可寫出面向對象的代碼,達到“無招勝有招”和“手中無劍,心中有劍”的境界。如:用C語言編寫的Linux、PostgreSQL處處展現着面向對象思想,本人也曾用PL/SQL編寫過面向對象的代碼,無非這部分代碼是編譯器幹,還是開發者自己幹。
領域驅動開發是本人認為目前階段較好的一種面向對象程式設計思維訓練的方法,推薦大家可以深入學習,後續本人将會對領域驅動開發的進行專題講解。
面向對象程式設計思想是一種很好的思考問題的方法,類似于工業化分工協作的大生産的方法。如果了解深刻,我們也可以像工業化大生産一樣生産軟體。
共勉。