?上世紀(jì)60年代爆發(fā)的軟件危機(jī)催生了軟件工程,人們寄希望于借助工程化的手段管理、設(shè)計(jì)、構(gòu)建和維護(hù)軟件,自此,聰明絕頂?shù)墓こ處煴阍谧非蟾篮密浖穆L路上艱苦求索。
開發(fā)語言經(jīng)歷了匯編、C、C++、Java、Erlang、Python;編程范式涵蓋了面向過程(POP)、面向?qū)ο螅∣OP)、泛型(GP)、函數(shù)式(FP);軟件架構(gòu)從單機(jī)到分布式到云原生,包括巨石,庫組件模塊服務(wù),分層,微服務(wù),MVC/ServiceMesh/Serverless等;而軟件工程思想和方法論則包括以生命周期管理為核心注重工序的瀑布模型(Waterfall Model),以需求進(jìn)化為核心注重迭代漸進(jìn)的敏捷開發(fā)(Agile Development),以邊界劃分和控制為核心注重領(lǐng)域建模的領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(DDD:Domain Driven Design)。
世界是繽紛復(fù)雜的,要把真實(shí)世界映射到虛擬軟件注定不會是一件容易的事,軟件開發(fā)是權(quán)衡抉擇的藝術(shù),譬如快速交付和安全生產(chǎn)常常背道而馳,開發(fā)效率和運(yùn)行效率總是難以一致。所以在軟件發(fā)展的歷史長河中,人們發(fā)明一種方法解決一個(gè)問題,而幾乎總是會引入另一個(gè)問題,軟件工程師不得不面對混沌不堪的世界。
面向過程(C)認(rèn)為一切皆過程,現(xiàn)實(shí)世界都可以封裝為一個(gè)個(gè)過程,通過過程串聯(lián)和編排模擬世界。但隨著軟件被大規(guī)模用于解決復(fù)雜的商業(yè)問題,這種范式被證明缺乏足夠的抽象,雖然函數(shù)可視為最小粒度的模塊化技術(shù),但依然無法掩蓋其模塊化能力的不足,過程和被操作數(shù)據(jù)分離也導(dǎo)致軟件偏離高內(nèi)聚低耦合的方向。
為了解決上述問題,面向?qū)ο蠓妒剑–+++/Java)被設(shè)計(jì)為通過對象建模世界,對象把屬性和方法封裝在一起,通過公開接口與外界交互,為軟件設(shè)計(jì)提供一種邏輯層面的模塊化手段;而且,對象與現(xiàn)實(shí)世界的事物很容易映射。對象通過組合表征更復(fù)雜的概念,通過接口泛化表達(dá)更抽象的概念。
泛型的動(dòng)機(jī)則更加簡單,需要一種語言機(jī)制,為解決跨越數(shù)據(jù)類型而提供標(biāo)準(zhǔn)容器的障礙,C++通過模板這種語言機(jī)制提供了參數(shù)化類型的能力,編譯期的類型檢查和類實(shí)例化既保障類型安全又提升執(zhí)行效率,但也增加編譯時(shí)間和損害可讀性,特別是模板元編程等新玩法的引入則讓事情更加復(fù)雜。
UML誕生于瀑布模型大行其道的時(shí)代,是獨(dú)立于具體程序設(shè)計(jì)語言的面向?qū)ο蠼9ぞ撸琔ML把面向?qū)ο箝_發(fā)分解為分析(OOA)、設(shè)計(jì)(OOD)和編碼(OOP)三個(gè)階段,該流程注重分析設(shè)計(jì)而輕視編碼實(shí)現(xiàn)。
由于設(shè)計(jì)和實(shí)現(xiàn)被劃分成2個(gè)相互鉗制的階段,所以開發(fā)過程會存在兩個(gè)模型,即一個(gè)顯化于UML圖紙中的設(shè)計(jì)模型,一個(gè)隱匿于軟件源碼中的實(shí)現(xiàn)模型,兩個(gè)階段兩個(gè)模型必然導(dǎo)致設(shè)計(jì)和編碼的割裂,設(shè)計(jì)和實(shí)施交予不同人實(shí)施看似有利于分工協(xié)作,實(shí)則增加了溝通成本,降低了交付效率。
學(xué)院派一度追求依照架構(gòu)師的UML設(shè)計(jì)圖就能自動(dòng)生成代碼,這看起來很美,而實(shí)際上這種目標(biāo)只在受限的情況下才能被滿足;而在日益復(fù)雜的商業(yè)軟件開發(fā)中,工程派感受到時(shí)間更多被消耗在開發(fā)和維護(hù)上,最終的交付件只能是源碼而非圖紙,所以開發(fā)人員只能轉(zhuǎn)向設(shè)計(jì)原則(SOLID)和設(shè)計(jì)模式(GOF)尋求慰藉,設(shè)計(jì)人員則頭頂“架構(gòu)師”的美名天馬行空。
瀑布模型的過程難以逆轉(zhuǎn),且只有到項(xiàng)目后期才能看到結(jié)果,針對瀑布的缺陷,敏捷開發(fā)試圖從改善軟件開發(fā)端到端的溝通方式入手,極限編程(XP)是一種實(shí)施敏捷開發(fā)的輕量級軟件工程方法學(xué),嘗試用一種螺旋式的方式演進(jìn),極限編程正視軟件活動(dòng)的復(fù)雜性,承認(rèn)需求在起步階段無法固化下來,主張開發(fā)人員應(yīng)該優(yōu)先將精力投入到代碼中,透過引入基本價(jià)值、原則、方法等概念來靈活應(yīng)對需求變更。
敏捷開發(fā)在互聯(lián)網(wǎng)的蓬勃發(fā)展中大放異彩,究其根本是因?yàn)榛ヂ?lián)網(wǎng)應(yīng)用的需求是動(dòng)態(tài)變化的,難以嚴(yán)格遵照沉重的傳統(tǒng)瀑布模型流程。
重原型實(shí)現(xiàn)的敏捷方法甚至被曲解為完全不需要設(shè)計(jì)和文檔的開發(fā)方式,敏捷的優(yōu)勢同時(shí)又成為它的弊端,忽視文檔的重要性,在人員流動(dòng)大的情況下無疑會加大維護(hù)的困難,且它在面對領(lǐng)域知識復(fù)雜的組織和應(yīng)用時(shí),敏捷開發(fā)也飽受質(zhì)疑。
隨著Web Service的應(yīng)用井噴,以Java為代表的新興語言攻城拔地,Controller->Service->Dao或者SOA設(shè)計(jì)搖身變化成行業(yè)的標(biāo)準(zhǔn)解,Service層扮演著上帝類,所有的邏輯都往里塞,充斥getter/setter DAO的貧血模式彌漫著惡臭味,基于數(shù)據(jù)驅(qū)動(dòng)的開發(fā)模式陷入了泥潭,領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)進(jìn)入了人們的視野。
2003年,Eric Evans提倡的領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)(DDD)視“領(lǐng)域內(nèi)核”為企業(yè)最重要資產(chǎn),主張通過通用語言(Ubiquitous Language)消除表達(dá)的不準(zhǔn)確性,領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)繼承并發(fā)展了敏捷開發(fā)。DDD把領(lǐng)域和設(shè)計(jì)歸為軟件設(shè)計(jì)的核心,讓業(yè)務(wù)人員和開發(fā)人員得到同樣的重視,建議合力捕捉充血的領(lǐng)域模型。DDD戰(zhàn)略設(shè)計(jì)從宏觀上確定限界上下文,而戰(zhàn)術(shù)設(shè)計(jì)在實(shí)現(xiàn)層面給出一些最佳實(shí)踐。
傳統(tǒng)模式秉持以數(shù)據(jù)(庫)為中心的理念,而領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)則轉(zhuǎn)向以領(lǐng)域模型為中心,這是根本性的設(shè)計(jì)轉(zhuǎn)變。DDD通過四重邊界劃分問題空間和解空間,確定核心、通用、支撐三類子領(lǐng)域,在界限上下文內(nèi)部,通過分層(基礎(chǔ)層-領(lǐng)域?qū)?應(yīng)用層-展現(xiàn)層)實(shí)現(xiàn)內(nèi)外隔離,應(yīng)用層形成了一種保護(hù)層,有效地隔離了業(yè)務(wù)復(fù)雜度與技術(shù)復(fù)雜度。將領(lǐng)域?qū)幼鳛檎麄€(gè)系統(tǒng)穩(wěn)定而內(nèi)聚的核心,是領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)的關(guān)鍵特征。
回顧軟件工程發(fā)展過程中涌現(xiàn)的各種主義,每一個(gè)流行的思潮都有一套自圓其說的理論,都聲稱完美解決了某些問題,但又無一例外的陷入另一個(gè)框架陷阱,但又都無一能夠終結(jié)軟件工程的無序設(shè)計(jì)。我們無法跳出自娛自樂般無休止重構(gòu)循環(huán)的怪圈,我們依然置身于充滿各種技術(shù)債的困境,所以,跳出各種框架模式的精神枷鎖,回過頭來,我們不妨重新審視設(shè)計(jì)的本質(zhì),我們不妨想一想應(yīng)對軟件復(fù)雜性的根本原則。
可將解決大規(guī)模復(fù)雜軟件問題的方法,簡單歸為幾點(diǎn):抽象,分解,隔離。
抽象就是歸類,歸類是為了復(fù)用。抽象的意義是通過表現(xiàn)找到事物背后的本質(zhì),抽象的目的是為了減輕認(rèn)知負(fù)擔(dān),避免重復(fù)思考和勞動(dòng),精簡問題空間,讓人關(guān)注更高層次的事物,建模是提煉心智模型的過程,本質(zhì)就是一種抽象。
分解是把一個(gè)復(fù)雜問題分割為更小的易于解決的小問題,拆分問題的過程即是簡化問題的過程,問題分解之后,還需要協(xié)作,這其實(shí)就是分治的理念,庫、組件化、微服務(wù)無不閃爍著分治理念的智慧的光芒。拆分有兩種方式:技術(shù)維度和業(yè)務(wù)維度,微服務(wù)和DDD就是從業(yè)務(wù)維度做問題拆分。拆分可以遵循AKF原則和康威定律,高內(nèi)聚低耦合是評價(jià)拆分好壞的標(biāo)準(zhǔn),讓上帝的歸上帝,讓凱撒的歸凱撒。
隔離是為了解耦,建立松耦合的系統(tǒng)一直是工程師們孜孜以求的目標(biāo),分層是實(shí)現(xiàn)隔離的有效手段,每層專注于自己的功能實(shí)現(xiàn),上層使用下層的能力,下層為上層提供服務(wù),上下層之間通過約定的接口交互,不緊鄰的層之間完全透明。外部世界的規(guī)則是契約、通信以及系統(tǒng)級別的架構(gòu)風(fēng)格和模式,而內(nèi)部世界的規(guī)則是分層、協(xié)作以及類級別的設(shè)計(jì)分格和模式。
在軟件變革的滾滾洪流中,軟件工程的先驅(qū)和賢哲們,提出了各種各樣的編程思想和方法論,但無一從根本上徹底解決問題,《人月神話》第16章提出,因?yàn)檐浖こ淌浅墢?fù)雜的系統(tǒng),所以斷言沒有銀彈,不僅沒有包治百病的靈藥,更指出在未來十年不可能有提升十倍效率的方法。
回顧歷史,每一種完美方案都從懟已有的方案和宣稱解決所有問題開始,然后傳播布道,把大眾帶入自己精心設(shè)計(jì)的邏輯閉環(huán),然后追隨者以一種宗教般的虔誠,將理論生搬硬套到項(xiàng)目中去,最后交付的代碼依然充斥各種模糊不清、污濁不堪,任何演進(jìn)都可能便引起偶然不變性的瞬間坍塌,剩下一地雞毛,而那些新穎的理論,最終都會像裊煙一樣,飄散在歷史的浩瀚天空中。
古人云:人生而無知,卻并不愚蠢,是教育使人愚蠢。古人又云:學(xué)而不思則怠。所以,我們應(yīng)該意識到思辨的重要性,對于知識,我們學(xué)習(xí)它研究它,但不盲從它。那對待軟件工程,我們應(yīng)該秉持怎樣的原則呢?
首先,人是關(guān)鍵,軟件開發(fā)沒有終極解,因?yàn)檐浖褪侨怂枷氲耐饣?,而人本身充滿缺陷。我們必須認(rèn)可人這種生物在抽象過程中的一些必然缺陷,以及人抽象能力的差異,這將意味著,相比于規(guī)則和流程,人其實(shí)才是軟件實(shí)施過程中的靈魂,思想和法則可以給人提供指引,但它們無法神奇的解決軟件工程中的所有問題,影響軟件開發(fā)質(zhì)量的關(guān)鍵因素是人,而不是設(shè)計(jì)方法。注重形式而不是內(nèi)容,注重文檔而非交付的代碼,都是本末倒置的。
其二,實(shí)事求是,具體問題具體分析,軟件涵蓋的范圍實(shí)在太廣的,這就意味著每一種具體實(shí)施細(xì)則都有它的局限性,不能用僵化的標(biāo)準(zhǔn)困住手腳,不可拘泥于規(guī)則而使之成為教條,不可用倚天劍剪指甲,不要用屠龍刀剃胡子。比如最簡單的業(yè)務(wù)CRUD模型可能就夠了,而有些可能適用CQRS、六邊形架構(gòu),有些貧血領(lǐng)域?qū)ο笠部梢?,有些可能事件風(fēng)暴模式更好,有時(shí)候數(shù)據(jù)和操作應(yīng)該分離,甚至讀寫也應(yīng)該分離,而有時(shí)候數(shù)據(jù)和操作封裝在一起更好,脫離實(shí)際的牛刀殺雞只會徒增笑耳。
第三,幾乎任何語言和技術(shù)都有好壞的兩面,都有適用性,比如C,它雖然欠缺抽象能力,但是它的核心語法集非常簡單,簡單意味著聚焦和可靠,意味著對程序員的要求更低,你只需要掌握幾十年不變的少數(shù)幾十個(gè)STD C API便能構(gòu)建所有應(yīng)用,但你必須認(rèn)識到它在抽象能力和開發(fā)效率上的不足。而C++雖然有良好的抽象能力,但它的語法集太龐大,而且似乎很難約束大家都在最小的公共知識面行事,但如果能夠達(dá)成共識,又或者大家水平都比較高,它編寫的程序確實(shí)有更好的可維護(hù)性。
第四,紀(jì)律!對,紀(jì)律才是關(guān)鍵,一套方法體系不管有多么的完美,如果團(tuán)隊(duì)不能嚴(yán)格地執(zhí)行方法體系規(guī)定的紀(jì)律,都是空談。無論是整潔編碼還是架構(gòu)設(shè)計(jì)還是敏捷開發(fā)還是領(lǐng)域建模,只有持之以恒的一致性的遵守紀(jì)律,用紀(jì)律施加約束,才能持續(xù)改進(jìn)質(zhì)量。
最后,雖然沒有銀彈,但我們不應(yīng)該過于悲觀,軟件工程一直在迂回中前進(jìn),每進(jìn)一步,就能夠解決一大片問題,同時(shí)引入一些可控的副作用,但宏觀上來看,軟件工程還是伴隨人類社會在不斷進(jìn)步。
所以,何不嘗試接受不完美?
聯(lián)系客服