軟件架構(gòu)設(shè)計(jì)的核心:抽象與模型抒巢、“戰(zhàn)略編程”

0. 引子:人類怎樣應(yīng)對復(fù)雜性贫贝?

復(fù)雜性

在任何程序(可以向外延伸到其他很多領(lǐng)域)的生命周期中,復(fù)雜性都會(huì)不可避免地增加蛉谜。
程序越大稚晚,工作的人越多,管理復(fù)雜性就越困難型诚,程序員在修改系統(tǒng)時(shí)將所有相關(guān)因素牢記在心中變得越來越難客燕;這會(huì)減慢開發(fā)速度并導(dǎo)致錯(cuò)誤,從而進(jìn)一步延緩開發(fā)速度并增加成本狰贯。

很多大型系統(tǒng)的本質(zhì)問題是復(fù)雜性問題也搓,數(shù)百個(gè)甚至更多的微服務(wù)相互調(diào)用/依賴,組成一個(gè)組件數(shù)量大涵紊、行為復(fù)雜慕爬、時(shí)刻在變動(dòng)(發(fā)布寞冯、配置變更)當(dāng)中的動(dòng)態(tài)的剂公、復(fù)雜的系統(tǒng)媚媒。

如果逢倍,我們將領(lǐng)域問題的復(fù)雜度與技術(shù)細(xì)節(jié)的復(fù)雜度混合在了一起郊楣,這最終將導(dǎo)致——整體復(fù)雜度的指數(shù)級增長骤竹。

復(fù)雜性的一個(gè)衡量維度:

  • 可維護(hù)性/可修改性
  • 一致性
  • 可讀性/清晰性
  • 可測試性

降低系統(tǒng)復(fù)雜性:一致性

Singe Source of Truth(SSO)

一致性是降低系統(tǒng)復(fù)雜性并使其行為更明顯的強(qiáng)大工具乒验。如果系統(tǒng)是一致的尿贫,則意味著相似的事情以相似的方式完成电媳,而不同的事情則以不同的方式完成。一致性會(huì)產(chǎn)生認(rèn)知影響力:一旦您了解了某個(gè)地方的工作方式庆亡,就可以使用該知識立即了解其他使用相同方法的地方匾乓。如果系統(tǒng)的實(shí)施方式不一致,則開發(fā)人員必須分別了解每種情況又谋。這將花費(fèi)更多時(shí)間拼缝。

一致性減少了錯(cuò)誤。如果系統(tǒng)不一致彰亥,則實(shí)際上兩種情況可能不同咧七,但兩種情況可能看起來相同。開發(fā)人員可能會(huì)看到一個(gè)看起來很熟悉的模式任斋,并根據(jù)以前對該模式的遭遇做出錯(cuò)誤的假設(shè)继阻。另一方面,如果系統(tǒng)是一致的,則基于熟悉情況的假設(shè)將是安全的瘟檩。一致性允許開發(fā)人員以更少的錯(cuò)誤來更快地工作抹缕。

一致性是投資心態(tài)的另一個(gè)例子。確保一致性的工作將需要一些額外的工作:確定約定墨辛,創(chuàng)建自動(dòng)檢查程序卓研,尋找類似情況以模仿新代碼,以及進(jìn)行代碼審查以教育團(tuán)隊(duì)睹簇。這項(xiàng)投資的回報(bào)是您的代碼將更加明顯鉴分。開發(fā)人員將能夠更快,更準(zhǔn)確地了解代碼的行為带膀,這將使他們能夠以更少的錯(cuò)誤來更快地工作志珍。

一致性示例

一致性可以應(yīng)用于系統(tǒng)中的許多級別。這里有一些例子垛叨。

編碼樣式伦糯。如今,開發(fā)組織通常擁有樣式指南嗽元,這些樣式指南將程序結(jié)構(gòu)限制在編譯器所強(qiáng)制執(zhí)行的規(guī)則之外×哺伲現(xiàn)代風(fēng)格指南解決了一系列問題,例如縮進(jìn)剂癌,大括號放置淤翔,聲明順序,命名佩谷,注釋以及對認(rèn)為危險(xiǎn)的語言功能的限制旁壮。樣式指南使代碼更易于閱讀,并且可以減少某些類型的錯(cuò)誤谐檀。
接口抡谐。具有多個(gè)實(shí)現(xiàn)的接口是一致性的另一個(gè)示例。一旦了解了接口的一種實(shí)現(xiàn)桐猬,其他任何實(shí)現(xiàn)都將變得更易于理解麦撵,因?yàn)槟呀?jīng)知道它將必須提供的功能。
設(shè)計(jì)模式溃肪。設(shè)計(jì)模式是某些常見問題的普遍接受的解決方案免胃,例如用于用戶界面設(shè)計(jì)的模型視圖控制器方法。如果您可以使用現(xiàn)有的設(shè)計(jì)模式來解決問題惫撰,則實(shí)現(xiàn)會(huì)更快地進(jìn)行羔沙,更有可能起作用,并且您的代碼對讀者來說也會(huì)更明顯润绎。
不變量撬碟。不變式是始終為真的變量或結(jié)構(gòu)的屬性诞挨。例如,存儲(chǔ)文本行的數(shù)據(jù)結(jié)構(gòu)可能會(huì)強(qiáng)制要求每行以換行符終止呢蛤。不變式減少了代碼中必須考慮的特殊情況的數(shù)量惶傻,并使推理行為的方式變得更加容易。

確保一致性

一致性很難維護(hù)其障,尤其是當(dāng)許多人長時(shí)間從事一個(gè)項(xiàng)目時(shí)银室。一組人可能不了解另一組中建立的約定。新來者不了解規(guī)則励翼,因此他們無意間違反了約定并創(chuàng)建了與現(xiàn)有約定沖突的新約定蜈敢。以下是建立和保持一致性的一些技巧:

  • 文檔規(guī)范。創(chuàng)建一個(gè)列出最重要的總體約定的文檔汽抚,例如編碼樣式準(zhǔn)則抓狭。將文檔放置在開發(fā)人員可能會(huì)看到的位置,例如項(xiàng)目 Wiki 上的顯眼位置造烁。鼓勵(lì)新成員加入小組閱讀文檔否过,并鼓勵(lì)現(xiàn)有人員不時(shí)審閱該文檔。Web 上已經(jīng)發(fā)布了來自各個(gè)組織的一些樣式指南惭蟋;考慮從其中之一開始苗桂。對于局部性更強(qiáng)的約定,例如不變式告组,請?jiān)诖a中找到合適的位置進(jìn)行記錄煤伟。如果您不寫下約定,那么其他人不太可能會(huì)遵循它們木缝。

  • 執(zhí)行機(jī)制便锨。即使有好的文檔,開發(fā)人員也很難記住所有約定氨肌。實(shí)施約定的最佳方法是編寫一個(gè)檢查違規(guī)的工具鸿秆,并確保除非通過檢查程序酌畜,否則代碼無法提交到存儲(chǔ)庫怎囚。自動(dòng)檢查器對于底層語法約定特別有用。

1. 模型:抽象與分層

“無名桥胞,萬物之始也; 有名恳守,萬物之母也”。(老子的《道德經(jīng)》第一章)
譯文:無贩虾,可以用來表示天地渾沌未開之際的狀況催烘,而有,則是宇宙萬物產(chǎn)生之本原的命
名缎罢。

抽象(Abstraction)

抽象的使用是計(jì)算機(jī)科學(xué)中最為重要的概念之一伊群。例如考杉,為一組函數(shù)規(guī)定一個(gè)簡單的應(yīng)用程序接口(API)就是一個(gè)很好的編程習(xí)慣,程序員無需了解它內(nèi)部的工作便可以使用這些代碼舰始。不同的編程語言提供不同形式和等級的抽象支持崇棠,例如 Java 類的聲明和 C 語言的函數(shù)原型。操作系統(tǒng)中也存在著很多的抽象:

在處理器里丸卷,指令集結(jié)構(gòu)提供了對實(shí)際處理器硬件的抽象枕稀。使用這個(gè)抽象,機(jī)器代碼程序表現(xiàn)得就好像它是運(yùn)行在一個(gè)一次只執(zhí)行一條指令的處理器上谜嫉。底層的硬件比抽象描述的要復(fù)雜精細(xì)得多萎坷,它并行地執(zhí)行多條指令,但又總是與那個(gè)簡單有序的模型保持一致沐兰。只要執(zhí)行模型一樣哆档,不同的處理器實(shí)現(xiàn)也能執(zhí)行同樣的機(jī)器代碼,而又提供不同的開銷和性能住闯。

文件是對 IO 的抽象虐呻,虛擬存儲(chǔ)器是對程序存儲(chǔ)器的抽象,而進(jìn)程是對一個(gè)正在運(yùn)行的程序的抽象寞秃。我們再增加一個(gè)新的抽象:虛擬機(jī)斟叼,它提供對整個(gè)計(jì)算機(jī)(包括操作系統(tǒng)、處理器和程序)的抽象春寿。虛擬機(jī)的思想是 IBM 在 20 世紀(jì) 60 年代提出來的朗涩,但是最近才顯示出其管理計(jì)算機(jī)方式上的優(yōu)勢,因?yàn)橐恍┯?jì)算機(jī)必須能夠運(yùn)行為不同操作系統(tǒng)(例如绑改,Microsoft Windows谢床、MacOS 和 Linux)或同一操作系統(tǒng)的不同版本而設(shè)計(jì)的程序。

模塊化設(shè)計(jì)

抽象與模塊化設(shè)計(jì)的思想緊密相關(guān)厘线。抽象是實(shí)體的簡化視圖识腿,其中省略了不重要的細(xì)節(jié)。抽象是有用的造壮,因?yàn)樗鼈兪刮覀兏菀姿伎己筒倏v復(fù)雜的事物渡讼。在模塊化編程中,每個(gè)模塊以其接口的形式提供抽象耳璧。該接口提供了模塊功能的簡化視圖成箫;從模塊抽象的角度來看,實(shí)現(xiàn)的細(xì)節(jié)并不重要旨枯,因此在接口中將其省略蹬昌。

在抽象的定義中,“無關(guān)緊要”一詞至關(guān)重要攀隔。從抽象中忽略的不重要的細(xì)節(jié)越多越好皂贩。但是栖榨,如果細(xì)節(jié)不重要,則只能將其從抽象中省略明刷。常見的不當(dāng)抽象可能包含以下兩種:

首先治泥,它可以包含并非真正重要的細(xì)節(jié)。當(dāng)這種情況發(fā)生時(shí)遮精,它會(huì)使抽象變得不必要的復(fù)雜居夹,從而增加了使用抽象的開發(fā)人員的認(rèn)知負(fù)擔(dān)。
第二個(gè)錯(cuò)誤是抽象忽略了真正重要的細(xì)節(jié)本冲。這導(dǎo)致模糊不清:僅查看抽象的開發(fā)人員將不會(huì)獲得正確使用抽象所需的全部信息准脂。忽略重要細(xì)節(jié)的抽象是錯(cuò)誤的抽象:它可能看起來很簡單,但實(shí)際上并非如此檬洞。
例如狸膏,考慮一個(gè)文件系統(tǒng)。文件系統(tǒng)提供的抽象省略了許多細(xì)節(jié)添怔,例如用于選擇存儲(chǔ)設(shè)備上的哪些塊用于給定文件中的數(shù)據(jù)的機(jī)制湾戳。這些詳細(xì)信息對于文件系統(tǒng)的用戶而言并不重要(只要系統(tǒng)提供足夠的性能即可)。但是广料,文件系統(tǒng)實(shí)現(xiàn)的一些細(xì)節(jié)對用戶很重要砾脑。大多數(shù)文件系統(tǒng)將數(shù)據(jù)緩存在主內(nèi)存中,并且它們可能會(huì)延遲將新數(shù)據(jù)寫入存儲(chǔ)設(shè)備以提高性能艾杏。一些應(yīng)用程序(例如數(shù)據(jù)庫)需要確切地知道何時(shí)將數(shù)據(jù)寫入存儲(chǔ)設(shè)備韧衣,因此它們可以確保在系統(tǒng)崩潰后將保留數(shù)據(jù)。因此购桑,將數(shù)據(jù)刷新到輔助存儲(chǔ)的規(guī)則必須在文件系統(tǒng)的接口中可見畅铭。

我們不僅依靠抽象來管理復(fù)雜性,而且不僅在編程中勃蜘,而且在日常生活中無處不在硕噩。微波爐包含復(fù)雜的電子設(shè)備,可將交流電轉(zhuǎn)換為微波輻射并將該輻射分布到整個(gè)烹飪腔中缭贡。幸運(yùn)的是炉擅,用戶看到了一個(gè)簡單得多的抽象,它由幾個(gè)按鈕控制微波的定時(shí)和強(qiáng)度匀归。汽車提供了一種簡單的抽象概念坑资,使我們可以在不了解電動(dòng)機(jī),電池電源管理穆端,防抱死制動(dòng),巡航控制等機(jī)制的情況下駕駛它們仿便。

分層存儲(chǔ)模型

另外一個(gè)典型的抽象模型体啰,就是計(jì)算機(jī)的存儲(chǔ)管理模型攒巍。

我們知道,在由半導(dǎo)體器件荒勇、電路板組成的計(jì)算硬件體系中柒莉,根本沒有操作系統(tǒng)、TCP/IP沽翔、內(nèi)存分配兢孝、垃圾回收等等概念模型。聰明的人類(這些人通常就是計(jì)算機(jī)科學(xué)家了)仅偎,就是靠著杰出的想象力與抽象能力跨蟹,設(shè)計(jì)出了計(jì)算機(jī)存儲(chǔ)分層抽象模型:

一個(gè)32位操作系統(tǒng)的例子。其中橘沥,1GB為操作系統(tǒng)的內(nèi)核空間窗轩,用戶無法更改,這部分不用管它座咆;剩下的3GB位用戶的內(nèi)存空間痢艺,這是供用戶程序使用的。

分層(Layering)

想必介陶,分層是宇宙創(chuàng)造萬物的方式堤舒。從地球構(gòu)造到雞蛋構(gòu)造,從太陽系組成到細(xì)胞結(jié)構(gòu)哺呜,無不如此植酥。

地球分層構(gòu)造圖:


雞蛋結(jié)構(gòu)圖:


太陽系:


細(xì)胞結(jié)構(gòu):

操作系統(tǒng)分層圖:

軟件系統(tǒng)由層組成,其中較高的層使用較低層提供的功能弦牡。例如:

在文件系統(tǒng)中友驮,最上層實(shí)現(xiàn)文件抽象。文件由可變長度的字節(jié)數(shù)組組成驾锰,可以通過讀寫可變長度的字節(jié)范圍來更新該字節(jié)卸留。文件系統(tǒng)的下一個(gè)下一層在固定大小的磁盤塊的內(nèi)存中實(shí)現(xiàn)了高速緩存。調(diào)用者可以假定經(jīng)常使用的塊將保留在內(nèi)存中椭豫,以便可以快速訪問它們耻瑟。最低層由設(shè)備驅(qū)動(dòng)程序組成,它們在輔助存儲(chǔ)設(shè)備和內(nèi)存之間移動(dòng)塊赏酥。

在諸如 TCP 的網(wǎng)絡(luò)傳輸協(xié)議中喳整,最頂層提供的抽象是從一臺(tái)機(jī)器可靠地傳遞到另一臺(tái)機(jī)器的字節(jié)流。此級別在較低級別上構(gòu)建裸扶,該級別可以盡最大努力在計(jì)算機(jī)之間傳輸有限大小的數(shù)據(jù)包:大多數(shù)數(shù)據(jù)包將成功交付框都,但某些數(shù)據(jù)包可能會(huì)丟失或亂序交付。

網(wǎng)絡(luò)分層協(xié)議

從最底層的物理鏈路層層層向上封裝抽象呵晨,解決了復(fù)雜的網(wǎng)絡(luò)通信的問題魏保。同樣的熬尺,任何復(fù)雜的問題,通過分層最終總能夠回歸最本質(zhì)谓罗、最簡單粱哼。

DDD 領(lǐng)域分層架構(gòu)

DDD 分層架構(gòu)遵循了“關(guān)注點(diǎn)分離”原則,將屬于業(yè)務(wù)邏輯的關(guān)注點(diǎn)放到:
1檩咱、領(lǐng)域?qū)樱―omain Layer)中揭措,
2、而將支撐業(yè)務(wù)邏輯的技術(shù)實(shí)現(xiàn)放到基礎(chǔ)設(shè)施層(Infrastructure Layer)中刻蚯。
3绊含、應(yīng)用層(Application Layer),扮演了雙重角色芦倒。一方面它作為業(yè)務(wù)邏輯的外觀(Facade)艺挪,暴露了能夠體現(xiàn)業(yè)務(wù)用例的應(yīng)用服務(wù)接口;另一方面它又是業(yè)務(wù)邏輯與技術(shù)實(shí)現(xiàn)的粘合劑兵扬,實(shí)現(xiàn)二者之間的協(xié)作麻裳。

下圖“分層架構(gòu)“展現(xiàn)的就是一個(gè)典型的領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)分層架構(gòu)。藍(lán)色區(qū)域的內(nèi)容與業(yè)務(wù)邏輯有關(guān)器钟,灰色區(qū)域的內(nèi)容與技術(shù)實(shí)現(xiàn)有關(guān)津坑,二者涇渭分明,然后匯合在應(yīng)用層傲霸。應(yīng)用層確定了業(yè)務(wù)邏輯與技術(shù)實(shí)現(xiàn)的邊界疆瑰,通過直接依賴或者依賴注入(DI,Dependency Injection)的方式將二者結(jié)合起來昙啄。

從上到下的層次隔離

為了將我們的應(yīng)用部署到服務(wù)器上穆役,我們需要為其配置一個(gè)運(yùn)行環(huán)境。從底層到頂層有這樣的運(yùn)行環(huán)境及容器:

  1. 隔離硬件:虛擬機(jī)
  2. 隔離操作系統(tǒng):容器虛擬化
  3. 隔離底層:Servlet 容器
  4. 隔離依賴版本:虛擬環(huán)境
  5. 隔離運(yùn)行環(huán)境:語言虛擬機(jī)
  6. 隔離語言:DSL

實(shí)現(xiàn)上這是一個(gè)請求的處理過程梳凛,一個(gè) HTTP 請求會(huì)先到達(dá)你的主機(jī)耿币。如果你的主機(jī)上運(yùn)行著多個(gè)虛擬機(jī)實(shí)例,那么請求就會(huì)來到這個(gè)虛擬機(jī)上韧拒。又或者是如果你是在 Docker 這一類容器里運(yùn)行你的程序的話淹接,那么也會(huì)先到達(dá) Docker。隨后這個(gè)請求就會(huì)交由 HTTP 服務(wù)器來處理叛溢,如 Apache塑悼、Nginx,這些 HTTP 服務(wù)器再將這些請求交由對應(yīng)的應(yīng)用或腳本來處理楷掉。隨后將交由語言底層的指令來處理厢蒜。

2.靜態(tài)視角:結(jié)構(gòu)

從結(jié)構(gòu)開始

什么是結(jié)構(gòu)(Structure)?結(jié)構(gòu),是由組成整體的各部分的搭配和安排郭怪。古人寫毛筆字支示,有云:“結(jié)構(gòu)圓備如篆法刊橘,飄颺灑落如章草鄙才。”(晉·衛(wèi)夫人《筆陣圖》)現(xiàn)代人做軟件結(jié)構(gòu)設(shè)計(jì)促绵,依然追尋著這樣一種美感——簡潔攒庵、優(yōu)雅、小巧玲瓏若珍珠寶石一般的美败晴。

在軟件架構(gòu)領(lǐng)域浓冒,“結(jié)構(gòu)”包括軟件元素,它們之間的關(guān)系尖坤,元素和關(guān)系的屬性稳懒,以及每個(gè)元素的引入和配置的基本原理(ISO/IEC 42010:20072)。

這里定義了架構(gòu)的三要素:職責(zé)明確的模塊或者組件慢味、組件間明確的關(guān)聯(lián)關(guān)系场梆、約束和指導(dǎo)原則。

軟件系統(tǒng)的架構(gòu)是一種隱喻纯路,類似于建筑物的體系結(jié)構(gòu)或油,是一種整體與局部關(guān)系的抽象描述,架構(gòu)是軟件系統(tǒng)內(nèi)部設(shè)計(jì)中最重要而又模糊的方面驰唬。有系統(tǒng)的地方就需要架構(gòu)顶岸,大到航空飛機(jī),小到一個(gè)電商系統(tǒng)里面的一個(gè)功能組件叫编,都需要設(shè)計(jì)和架構(gòu)辖佣。

架構(gòu),
(1)是對系統(tǒng)中的實(shí)體搓逾,以及實(shí)體之間的關(guān)系卷谈,所進(jìn)行的抽象描述,
(2)是對事物的功能與形式元素之間的對應(yīng)關(guān)系恃逻,所做的分配雏搂,
(3)是對元素之間的關(guān)系,以及元素同周邊環(huán)境之間的關(guān)系所做的定義寇损。

架構(gòu)能將目標(biāo)系統(tǒng)按某個(gè)原則進(jìn)行切分凸郑,切分的原則,是要便于不同的角色進(jìn)行并行工作矛市,結(jié)構(gòu)良好的創(chuàng)造活動(dòng)要優(yōu)于毫無結(jié)構(gòu)的創(chuàng)造活動(dòng)芙沥。

為什么需要架構(gòu)?

舉一個(gè)蓋房子的例子。我們偶爾會(huì)從新聞上聽到某某房子倒塌而昨、高架塌陷等悲慘事件救氯,但是想想背后的原因,是不是因?yàn)榉孔痈韬蛄航Y(jié)構(gòu)不合理着憨,施工偷工減料,質(zhì)量檢查沒有履行職責(zé)等原因?qū)е拢?/p>

這個(gè)在軟件工程領(lǐng)域务嫡,道理是相通的甲抖。一個(gè)沒有經(jīng)過合理設(shè)計(jì)就匆忙開發(fā)上線的系統(tǒng),遲早要還“技術(shù)債”心铃。

架構(gòu)并不由系統(tǒng)的功能決定准谚,而是由系統(tǒng)的非功能屬性決定。

架構(gòu)需要:
1去扣、控制系統(tǒng)復(fù)雜性柱衔,將核心業(yè)務(wù)邏輯和技術(shù)細(xì)節(jié)的分離與解耦。
2愉棱、保證系統(tǒng)高可用唆铐。
3、提升團(tuán)隊(duì)整體的研發(fā)效能羽氮。

架構(gòu)師的職責(zé)是:
1或链、努力訓(xùn)練自己的思維,用它去理解復(fù)雜的系統(tǒng)档押,
2澳盐、通過合理的分解和抽象,理解并解析需求令宿,
3叼耙、創(chuàng)建有用的模型,
4粒没、確認(rèn)筛婉、細(xì)化并擴(kuò)展模型,管理架構(gòu)癞松;
5爽撒、進(jìn)行系統(tǒng)分解形成整體架構(gòu),
6响蓉、能夠正確的技術(shù)選型硕勿,
7、能夠制定技術(shù)規(guī)格說明并有效推動(dòng)實(shí)施落地枫甲。

3.動(dòng)態(tài)視角:運(yùn)動(dòng)變化的關(guān)系

戰(zhàn)略與戰(zhàn)術(shù):動(dòng)態(tài)演化的視角

大多數(shù)程序員的編程行為源武,都是戰(zhàn)術(shù)思維方式扼褪,著眼于使功能盡快運(yùn)行。
但是粱栖,如果您想要一個(gè)好的設(shè)計(jì)话浇,則必須采取更具戰(zhàn)略性的方法,在此上花費(fèi)時(shí)間來制作干凈的設(shè)計(jì)并解決問題闹究。

戰(zhàn)術(shù)編程(Tactical Programming)

在戰(zhàn)術(shù)編程方法中幔崖,核心關(guān)注點(diǎn)是,使某些功能正常工作跋核,例如新功能或錯(cuò)誤修復(fù)岖瑰。
乍一看叛买,這似乎是完全合理的:還有什么比編寫有效的代碼更重要的呢砂代?
但是,戰(zhàn)術(shù)編程幾乎不可能產(chǎn)生出良好的系統(tǒng)設(shè)計(jì)率挣。
戰(zhàn)術(shù)編程的問題是它是短視的刻伊。
如果您是戰(zhàn)術(shù)編程人員,那么您將只是盡快完成任務(wù)椒功,您不會(huì)花費(fèi)太多時(shí)間來尋找最佳設(shè)計(jì)捶箱。您只想盡快使某件事起作用。您告訴自己动漾,可以增加一些復(fù)雜性或引入一兩個(gè)小錯(cuò)誤丁屎,如果這樣可以使當(dāng)前任務(wù)更快地完成,則可以旱眯。

如果您進(jìn)行戰(zhàn)術(shù)編程晨川,則每個(gè)編程任務(wù)都會(huì)帶來一些此類復(fù)雜性。
為了快速完成當(dāng)前任務(wù)删豺,他們每個(gè)人似乎都是一個(gè)合理的折衷方案共虑。
但是,復(fù)雜性迅速累積呀页,尤其是如果每個(gè)人都在戰(zhàn)術(shù)上進(jìn)行編程的時(shí)候妈拌。

不久之后,某些復(fù)雜性將開始引起問題蓬蝶,但是尘分,您會(huì)告訴(qi pian)自己,使下一個(gè)功能正常工作比返回并重構(gòu)現(xiàn)有代碼更為重要丸氛。從長遠(yuǎn)來看培愁,重構(gòu)可能會(huì)有所幫助,但是肯定會(huì)減慢當(dāng)前的任務(wù)雪位。

因此竭钝,您需要快速修補(bǔ)程序來解決遇到的任何問題梨撞。這只會(huì)增加復(fù)雜性,然后需要更多補(bǔ)丁香罐。

很快代碼變得一團(tuán)糟卧波,但是到現(xiàn)在為止,情況已經(jīng)很糟糕了庇茫,清理它需要花費(fèi)數(shù)月的時(shí)間港粱。您的日程安排無法容忍這種延遲,解決一個(gè)或兩個(gè)問題似乎并沒有太大的區(qū)別旦签,因此您只是在戰(zhàn)術(shù)上保持編程查坪。

幾乎每個(gè)軟件開發(fā)組織,都有至少一個(gè)將戰(zhàn)術(shù)編程發(fā)揮到極致的開發(fā)人員:戰(zhàn)術(shù)龍卷風(fēng)(The tactical tornado)宁炫。

戰(zhàn)術(shù)龍卷風(fēng)是一位多產(chǎn)的程序員偿曙,他抽出代碼的速度比其他人快得多,但完全以戰(zhàn)術(shù)方式工作羔巢。實(shí)施快速功能時(shí)望忆,沒有人能比戰(zhàn)術(shù)龍卷風(fēng)更快地完成任務(wù)。

在某些組織中竿秆,管理層將戰(zhàn)術(shù)龍卷風(fēng)視為英雄启摄。但是,戰(zhàn)術(shù)龍卷風(fēng)留下了毀滅的痕跡幽钢。他們很少被將來必須使用其代碼的工程師視為英雄歉备。通常,其他工程師必須清理戰(zhàn)術(shù)龍卷風(fēng)留下的混亂局面匪燕,這使得那些工程師(他們是真正的英雄)的進(jìn)步似乎比戰(zhàn)術(shù)龍卷風(fēng)慢蕾羊。

在戰(zhàn)術(shù)編程中,您將不斷增加一些復(fù)雜性谎懦,這些復(fù)雜性將來會(huì)引起問題肚豺。

戰(zhàn)略編程(Strategic programming)

成為一名優(yōu)秀的軟件設(shè)計(jì)師的第一步是要意識到僅工作代碼是不夠的。引入不必要的復(fù)雜性以更快地完成當(dāng)前任務(wù)是不可接受的界拦。最重要的是系統(tǒng)的長期結(jié)構(gòu)吸申。任何系統(tǒng)中的大多數(shù)代碼都是通過擴(kuò)展現(xiàn)有代碼庫編寫的,因此享甸,作為開發(fā)人員截碴,最重要的工作就是促進(jìn)這些將來的擴(kuò)展。因此蛉威,盡管您的代碼當(dāng)然必須工作日丹,但您不應(yīng)將“工作代碼”視為主要目標(biāo)。您的主要目標(biāo)必須是制作出出色的設(shè)計(jì)蚯嫌,并且這種設(shè)計(jì)也會(huì)起作用哲虾。這是戰(zhàn)略計(jì)劃丙躏。

戰(zhàn)略性編程需要一種投資心態(tài)。您必須花費(fèi)時(shí)間來改進(jìn)系統(tǒng)的設(shè)計(jì)束凑,而不是采取最快的方式來完成當(dāng)前的項(xiàng)目晒旅。這些投資會(huì)在短期內(nèi)讓您放慢腳步,但從長遠(yuǎn)來看會(huì)加快您的速度汪诉。一些投資將是積極的废恋。例如,值得花一些時(shí)間為每個(gè)新類找到一個(gè)簡單的設(shè)計(jì)扒寄。而不是實(shí)施想到的第一個(gè)想法鱼鼓,請嘗試幾種替代設(shè)計(jì)并選擇最簡潔的設(shè)計(jì)。試想一下將來可能需要更改系統(tǒng)的幾種方式该编,并確保設(shè)計(jì)容易迄本。編寫好的文檔是主動(dòng)投資的另一個(gè)例子。

如果您進(jìn)行戰(zhàn)略性編程上渴,則將不斷對系統(tǒng)設(shè)計(jì)進(jìn)行小幅改進(jìn)岸梨。

權(quán)衡:ROI

一開始,戰(zhàn)術(shù)性的編程方法將比戰(zhàn)略性方法更快地取得進(jìn)展稠氮。但是,在戰(zhàn)術(shù)方法下半开,復(fù)雜性積累得更快隔披,從而降低了生產(chǎn)率。

隨著時(shí)間的流逝寂拆,戰(zhàn)略方針會(huì)帶來更大的進(jìn)步奢米。注意:此圖僅用于定性說明;我不知道對曲線精確形狀的任何經(jīng)驗(yàn)測量纠永。

相反鬓长,如果您進(jìn)行戰(zhàn)術(shù)編程,則可以將第一個(gè)項(xiàng)目完成的速度提高 10%到 20%尝江,但是隨著時(shí)間的推移涉波,復(fù)雜性的累積會(huì)降低開發(fā)速度。不久之后炭序,您的編程速度至少會(huì)降低 10–20%。您將很快退回在開始時(shí)節(jié)省的所有時(shí)間,并且在系統(tǒng)的整個(gè)生命周期中脖卖,與采用策略性方法相比袱吆,您的開發(fā)速度將更加緩慢。

可持續(xù)迭代演化的系統(tǒng):演進(jìn)式架構(gòu)(Evolutionary Architecture)

與傳統(tǒng)的前期辜纲、重量級的企業(yè)架構(gòu)設(shè)計(jì)相比笨觅,我們建議采用演進(jìn)式架構(gòu)(Evolutionary Architecture)拦耐。它提供了企業(yè)架構(gòu)的好處,卻沒有試圖準(zhǔn)確預(yù)測未來所帶來的問題见剩。

演進(jìn)式架構(gòu)不需要猜測組件將如何被重用揩魂,而是支持適應(yīng)性,使用適當(dāng)?shù)某橄笈谖隆?shù)據(jù)庫遷移火脉、測試套件、持續(xù)集成和重構(gòu)來收獲系統(tǒng)內(nèi)發(fā)生的重用柒啤。

盡管我們盡了最大努力倦挂,但復(fù)雜度仍會(huì)隨著時(shí)間的推移而增加,但是更簡單的設(shè)計(jì)使我們能夠在復(fù)雜性壓倒性優(yōu)勢之前構(gòu)建更大担巩,功能更強(qiáng)大的系統(tǒng)方援。復(fù)雜性的應(yīng)對永遠(yuǎn)不會(huì)是一勞永逸,我們需要不斷地推陳出新涛癌,是動(dòng)態(tài)犯戏、漸進(jìn)的重塑自己對軟件系統(tǒng)的認(rèn)識,不斷認(rèn)識問題和尋找更優(yōu)解的持續(xù)迭代:

互聯(lián)網(wǎng)行業(yè)的軟件系統(tǒng)拳话,很難一開始就做出完美的設(shè)計(jì)先匪,通過一個(gè)個(gè)功能模塊衍生迭代,系統(tǒng)才會(huì)逐步成型弃衍;對于現(xiàn)存的系統(tǒng)呀非,也很難通過一個(gè)大動(dòng)作,一勞永逸地解決所有問題镜盯。系統(tǒng)設(shè)計(jì)是需要持續(xù)投入的工作岸裙,通過細(xì)節(jié)的積累,最終得到一個(gè)完善的系統(tǒng)速缆。因此降允,好的設(shè)計(jì)是日拱一卒的結(jié)果,在日常工作中要重視設(shè)計(jì)和細(xì)節(jié)的改進(jìn)艺糜。

  1. 通過使代碼更簡單和更清晰(Obvious)來消除復(fù)雜性剧董。例如: 減少特殊場景的處理,或變量命名一致性都能降低系統(tǒng)復(fù)雜性倦踢。代碼能夠描述程序的工作流程和結(jié)果送滞,卻很難描述開發(fā)人員的思路,而注釋和文檔可以辱挥。此外犁嗅,通過注釋和文檔,開發(fā)人員在不閱讀實(shí)現(xiàn)代碼的情況下晤碘,就可以理解程序的功能褂微,注釋間接促成了代碼抽象功蜓。好的注釋能夠幫助解決軟件復(fù)雜性問題,尤其是認(rèn)知負(fù)擔(dān)和不可知問題(Unknown Unknowns)宠蚂。

  2. 通過分層或者分模塊來封裝它式撼,對復(fù)雜問題的抽象然后分而治之,以便程序員可以在系統(tǒng)上工作而不會(huì)立即暴露其所有復(fù)雜性求厕。這種方法稱為模塊化設(shè)計(jì)著隆。在模塊化設(shè)計(jì)中,軟件系統(tǒng)分為模塊呀癣,例如面向?qū)ο笳Z言的類美浦。這些模塊被設(shè)計(jì)為彼此相對獨(dú)立,以便程序員可以在一個(gè)模塊上工作而不必了解其他模塊的細(xì)節(jié)项栏。

  3. 專業(yè)化分工和代碼復(fù)用浦辨,促成了軟件生產(chǎn)率的提升。比如硬件工程師沼沈、軟件工程師(底層流酬、應(yīng)用、不同編程語言)可以在無需了解對方技術(shù)背景的情況下進(jìn)行合作開發(fā)列另;同一領(lǐng)域服務(wù)可以支撐不同的上層應(yīng)用邏輯等等芽腾。其背后的思想,無非是通過將系統(tǒng)分成若干個(gè)水平層访递、明確每一層的角色和分工晦嵌,來降低單個(gè)層次的復(fù)雜性。同時(shí)拷姿,每個(gè)層次只要給相鄰層提供一致的接口,可以用不同的方法實(shí)現(xiàn)旱函,這就為軟件重用提供了支持响巢。分層是解決復(fù)雜性問題的重要原則。

  4. 與分層類似棒妨,分模塊是從垂直方向來分解系統(tǒng)踪古。分模塊最常見的應(yīng)用場景,是如今廣泛流行的微服務(wù)券腔。分模塊降低了單模塊的復(fù)雜性伏穆,但是也會(huì)引入新的復(fù)雜性,例如模塊與模塊的交互

4. 軟件架構(gòu)編年史

  • 20 世紀(jì) 50 年代
    • 非結(jié)構(gòu)化編程
    • ~1951 – 匯編
  • 20 世紀(jì) 60 年代
    • 結(jié)構(gòu)化編程
    • 分層: 用戶界面纷纫、業(yè)務(wù)邏輯數(shù)據(jù)存儲(chǔ)都在一層枕扫。
    • ~1958 – Algol
  • 20 世紀(jì) 70 年代
    • 過程式/函數(shù)式編程
    • ~1970 – Pascal
    • ~1972 – C
    • 1979MVC 模式(Model-View-Controller)
  • 20 世紀(jì) 80 年代
    • 面向?qū)ο缶幊?/strong> (但其思想在 20 世紀(jì) 60 年代晚期已經(jīng)第一次提出)
    • 分層: 兩層,第一層是用戶界面辱魁,第二層是業(yè)務(wù)邏輯和數(shù)據(jù)存儲(chǔ)
    • ~1980 – C++
    • CORBA – 通用物件請求代理架構(gòu)(盡管1991 年才推出第一個(gè)穩(wěn)定版烟瞧,但最早使用可以追溯到 20 世紀(jì) 80 年代)
    • ~1986 – Erlang
    • ~1987 – Perl
    • 1987 – PAC 即 HMVC 模式(Hierarchical Model-View-Controller)
    • 1988LSP(里氏替換原則) (~SOLID)
  • 20 世紀(jì) 90 年代
    • 分層: 三層诗鸭,第一層是用戶界面,第二層是業(yè)務(wù)邏輯(以及瀏覽器作為客戶端時(shí)的用戶界面展現(xiàn)邏輯)参滴,第三層是數(shù)據(jù)存儲(chǔ)
    • ~1991 – 消息總線
    • ~1991 – Python
    • 1992EBI 架構(gòu)(Entity-Boundary-Interactor) 即 EBC 或 EIC
    • ~1993 – Ruby
    • ~1995 – Delphi, Java, Javascript, PHP
    • 1996MVP 模式(Model-View-Presenter)
    • 1996OCP, ISP, DIP (~SOLID), REP, CRP, CCP, ADP
    • 1997 – SDP, SAP
    • ~1997面向方面編程
    • ~1997 – Web 服務(wù)
    • ~1997ESB – 企業(yè)服務(wù)總線 (盡管創(chuàng)造該術(shù)語的書籍 2004 年才出版强岸,但這個(gè)概念早已被使用)
  • 21 世紀(jì) 00 年代
    • 2002SRP (~SOLID)
    • 2003領(lǐng)域驅(qū)動(dòng)設(shè)計(jì)
    • 2005MVVM 模式(Model-View-ViewModel)
    • 2005端口和適配器架構(gòu)即六邊形架構(gòu)
    • 2006CQRS 與 ES (命令查詢職責(zé)分離與事件溯源)
    • 2008洋蔥架構(gòu)
    • 2009微服務(wù)(Netflix)
  • 21 世紀(jì) 10 年代
    • 2010DCI 架構(gòu)(Data-Context-Interaction)
    • 2012整潔架構(gòu)
    • 2014 – C4 模型

5. 編程哲學(xué):禪與計(jì)算機(jī)程序設(shè)計(jì)藝術(shù)

 美麗勝于丑陋。 
 顯式優(yōu)于隱式砾赔。 
 簡單勝于復(fù)雜蝌箍。 
 復(fù)雜勝于復(fù)雜。 
 扁平比嵌套好暴心。 
 疏勝于密妓盲。 
 可讀性很重要。 
 特殊情況不足以打破規(guī)則酷勺。 
 盡管本橙,實(shí)用第一,簡潔第二脆诉。 
 錯(cuò)誤永遠(yuǎn)不應(yīng)該無聲無息地過去甚亭。 
 除非明確沉默。 
 面對曖昧击胜,拒絕猜測的誘惑亏狰。 
 應(yīng)該有一種——最好只有一種——顯而易見的方法來做到這一點(diǎn)。 
 雖然這種方式一開始可能不明顯偶摔,除非你是荷蘭人暇唾。 
 現(xiàn)在總比沒有好。 
 雖然永遠(yuǎn)不會(huì)比現(xiàn)在好辰斋。 
 如果實(shí)現(xiàn)很難解釋策州,那就是一個(gè)壞主意。 
 如果實(shí)現(xiàn)容易解釋宫仗,這可能是一個(gè)好主意够挂。 
 命名空間是一個(gè)非常棒的主意——讓我們做更多這樣的事情吧!

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

6. 架構(gòu)師

架構(gòu)師藕夫,是一個(gè)既能掌控整體全局孽糖,又能洞悉局部瓶頸,并依據(jù)具體的業(yè)務(wù)場景毅贮,給出解決方案的團(tuán)隊(duì)領(lǐng)導(dǎo)型人物办悟。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市滩褥,隨后出現(xiàn)的幾起案子病蛉,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,013評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件铡恕,死亡現(xiàn)場離奇詭異琢感,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)探熔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評論 2 382
  • 文/潘曉璐 我一進(jìn)店門驹针,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人诀艰,你說我怎么就攤上這事柬甥。” “怎么了其垄?”我有些...
    開封第一講書人閱讀 152,370評論 0 342
  • 文/不壞的土叔 我叫張陵苛蒲,是天一觀的道長。 經(jīng)常有香客問我绿满,道長臂外,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,168評論 1 278
  • 正文 為了忘掉前任喇颁,我火速辦了婚禮漏健,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘橘霎。我一直安慰自己蔫浆,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,153評論 5 371
  • 文/花漫 我一把揭開白布姐叁。 她就那樣靜靜地躺著瓦盛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪外潜。 梳的紋絲不亂的頭發(fā)上原环,一...
    開封第一講書人閱讀 48,954評論 1 283
  • 那天,我揣著相機(jī)與錄音处窥,去河邊找鬼扮念。 笑死,一個(gè)胖子當(dāng)著我的面吹牛碧库,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播巧勤,決...
    沈念sama閱讀 38,271評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼嵌灰,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了颅悉?” 一聲冷哼從身側(cè)響起沽瞭,我...
    開封第一講書人閱讀 36,916評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎剩瓶,沒想到半個(gè)月后驹溃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體城丧,經(jīng)...
    沈念sama閱讀 43,382評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,877評論 2 323
  • 正文 我和宋清朗相戀三年豌鹤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了亡哄。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,989評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡布疙,死狀恐怖蚊惯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情灵临,我是刑警寧澤截型,帶...
    沈念sama閱讀 33,624評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站儒溉,受9級特大地震影響宦焦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜顿涣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,209評論 3 307
  • 文/蒙蒙 一波闹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧园骆,春花似錦舔痪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,199評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至晌涕,卻和暖如春滋捶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背余黎。 一陣腳步聲響...
    開封第一講書人閱讀 31,418評論 1 260
  • 我被黑心中介騙來泰國打工重窟, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人惧财。 一個(gè)月前我還...
    沈念sama閱讀 45,401評論 2 352
  • 正文 我出身青樓巡扇,卻偏偏與公主長得像,于是被迫代替她去往敵國和親垮衷。 傳聞我的和親對象是個(gè)殘疾皇子厅翔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,700評論 2 345

推薦閱讀更多精彩內(nèi)容