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