架構基礎之架構的背景,概念和目的(筆記類)

筆記類,指在學習課程、專欄過程中對個人有用內容的記錄和少許理解。

一、背景介紹

如果想要深入理解一個事物的本質,最好的方式就是去追尋這個事物出現的歷史背景和推動因素。一項技術/方法的出現一定是爲了解決之前某種技術/場景的痛點。

1.1 軟件開發歷史

1.1.1 機器語言(1940年之前)

最早的軟件開發使用的是“機器語言”,直接使用二進制碼 0 和 1 來表示機器可以識別的指令和數據。例如,在 8086 機器上完成“s=768+12288-1280”的數學運算,機器碼如下:

101100000000000000000011
000001010000000000110000
001011010000000000000101

不用多說,不管是當時的程序員,還是現在的程序員,第一眼看到這樣一串東西時,肯定是一頭霧水,因爲這實在是太難看懂了,這還只是一行運算,如果要輸出一個“hello world”,面對幾十上百行這樣的 0/1 串,眼睛都要花了!

如果讓你去排查問題和修改bug,真的無法想象····

歸納一下,機器語言的主要問題是三難:難寫、難讀、難改!

1.1.2 彙編語言(20世紀40年代)

爲了解決機器語言編寫、閱讀、修改複雜的問題,彙編語言應運而生。彙編語言又叫“符號語言”,用助記符代替機器指令的操作碼,用地址符號(Symbol)或標號(Label)代替指令或操作數的地址。

例如,爲了完成“將寄存器 BX 的內容送到 AX 中”的簡單操作,彙編語言和機器語言分別如下。

機器語言:1000100111011000
彙編語言:mov ax,bx

相比機器語言來說,彙編語言就清晰得多了。mov 是操作,ax 和 bx 是寄存器代號,mov ax,bx 語句基本上就是“將寄存器 BX 的內容送到 AX”的簡化版的翻譯,即使不懂彙編,單純看到這樣一串語言,至少也能明白大概意思。

彙編語言雖然解決了機器語言讀寫複雜的問題,但本質上還是面向機器的,因爲寫彙編語言需要我們精確瞭解計算機底層的知識。例如,CPU 指令、寄存器、段地址等底層的細節。這對於程序員來說同樣很複雜,因爲程序員需要將現實世界中的問題和需求按照機器的邏輯進行翻譯。例如,對於程序員來說,在現實世界中面對的問題是 4 + 6 = ?。而要用匯編語言實現一個簡單的加法運算,代碼如下:

.section .data
  a: .int 10
  b: .int 20
  format: .asciz "%d\n"
.section .text
.global _start
_start:
  movl a, %edx  
  addl b, %edx  
  pushl %edx
  pushl $format
  call printf
  movl $0, (%esp)
  call exit

這還只是實現一個簡單的加法運算所需要的彙編程序,可以想象一下,實現一個四則運算的程序會更加複雜,更不用說用匯編寫一個操作系統了!

除了編寫本身複雜,還有另外一個複雜的地方在於:不同 CPU 的彙編指令和結構是不同的。例如,Intel 的 CPU 和 Motorola 的 CPU 指令不同,同樣一個程序,爲 Intel 的 CPU 寫一次,還要爲 Motorola 的 CPU 再寫一次,而且指令完全不同。

1.1.3 高級語言(20世紀50年代)

爲了解決彙編語言的問題,計算機前輩們從 20 世紀 50 年代開始又設計了多個高級語言,最初的高級語言有下面幾個,並且這些語言至今還在特定的領域繼續使用。

  • Fortran:1955 年,名稱取自”FORmula TRANslator”,即公式翻譯器,由約翰·巴科斯(John
    Backus)等人發明。
  • LISP:1958 年,名稱取自”LISt Processor”,即枚舉處理器,由約翰·麥卡錫(John
    McCarthy)等人發明。Cobol:1959 年,名稱取自”Common Business Oriented
  • Language”,即通用商業導向語言,由葛麗絲·霍普(Grace Hopper)發明。

爲什麼稱這些語言爲“高級語言”呢?原因在於這些語言讓程序員不需要關注機器底層的低級結構和邏輯,而只要關注具體的問題和業務即可。

還是以 4 + 6=?這個加法爲例,如果用 LISP 語言實現,只需要簡單一行代碼即可:

(+ 4 6)

除此以外,通過編譯程序的處理,高級語言可以被編譯爲適合不同 CPU 指令的機器語言。程序員只要寫一次程序,就可以在多個不同的機器上編譯運行,無須根據不同的機器指令重寫整個程序。

1.1.4 第一次軟件危機和結構化設計(20世紀 60年代~20世紀70年代)

高級語言的出現,解放了程序員,但好景不長,隨着軟件的規模和複雜度的大大增加,20 世紀 60 年代中期開始爆發了第一次軟件危機,典型表現有軟件質量低下、項目無法如期完成、項目嚴重超支等,因爲軟件而導致的重大事故時有發生。例如,1963 年美國的水手一號火箭發射失敗事故,就是因爲一行 FORTRAN 代碼錯誤導致的。

軟件危機最典型的例子莫過於 IBM 的 System/360 的操作系統開發。佛瑞德·布魯克斯(Frederick P. Brooks, Jr.)作爲項目主管,率領 2000 多個程序員夜以繼日地工作,共計花費了 5000 人一年的工作量,寫出將近 100 萬行的源碼,總共投入 5 億美元,是美國的“曼哈頓”原子彈計劃投入的 1/4。儘管投入如此巨大,但項目進度卻一再延遲,軟件質量也得不到保障。布魯克斯後來基於這個項目經驗而總結的《人月神話》一書,成了暢銷的軟件工程書籍。

爲了解決問題,在 1968、1969 年連續召開兩次著名的 NATO 會議,會議正式創造了“軟件危機”一詞,並提出了針對性的解決方法“軟件工程”。雖然“軟件工程”提出之後也曾被視爲軟件領域的萬能鑰匙,但後來事實證明,軟件工程同樣無法根除軟件危機,只能在一定程度上緩解軟件危機。

差不多同一時間,“結構化程序設計”作爲另外一種解決軟件危機的方案被提了出來。艾茲赫爾·戴克斯特拉(Edsger Dijkstra)於 1968 年發表了著名的《GOTO 有害論》論文,引起了長達數年的論戰,並由此產生了結構化程序設計方法。同時,第一個結構化的程序語言 Pascal 也在此時誕生,並迅速流行起來。

結構化程序設計的主要特點是拋棄 goto 語句,採取“自頂向下、逐步細化、模塊化”的指導思想。結構化程序設計本質上還是一種面向過程的設計思想,但通過“自頂向下、逐步細化、模塊化”的方法,將軟件的複雜度控制在一定範圍內,從而從整體上降低了軟件開發的複雜度。結構化程序方法成爲了 20 世紀 70 年代軟件開發的潮流。

1.1.5 第二次軟件危機與面向對象(20世紀80年代)

結構化編程的風靡在一定程度上緩解了軟件危機,然而隨着硬件的快速發展,業務需求越來越複雜,以及編程應用領域越來越廣泛,第二次軟件危機很快就到來了。

第二次軟件危機的根本原因還是在於軟件生產力遠遠跟不上硬件和業務的發展。第一次軟件危機的根源在於軟件的“邏輯”變得非常複雜,而第二次軟件危機主要體現在軟件的“擴展”變得非常複雜。結構化程序設計雖然能夠解決(也許用“緩解”更合適)軟件邏輯的複雜性,但是對於業務變化帶來的軟件擴展卻無能爲力,軟件領域迫切希望找到新的銀彈來解決軟件危機,在這種背景下,面向對象的思想開始流行起來。

面向對象的思想並不是在第二次軟件危機後纔出現的,早在 1967 年的 Simula 語言中就開始提出來了,但第二次軟件危機促進了面向對象的發展。面向對象真正開始流行是在 20 世紀 80 年代,主要得益於 C++ 的功勞,後來的 Java、C# 把面向對象推向了新的高峯。到現在爲止,面向對象已經成爲了主流的開發思想。

雖然面向對象開始也被當作解決軟件危機的銀彈,但事實證明,和軟件工程一樣,面向對象也不是萬能的,而只是一種新的軟件方法而已。

1.2 軟件架構的歷史背景

雖然早在 20 世紀 60 年代,戴克斯特拉這位上古大神就已經涉及軟件架構這個概念了,但軟件架構真正流行卻是從 20 世紀 90 年代開始的,由於在 Rational 和 Microsoft 內部的相關活動,軟件架構的概念開始越來越流行了。

與之前的各種新方法或者新理念不同的是,“軟件架構”出現的背景並不是整個行業都面臨類似相同的問題,“軟件架構”也不是爲了解決新的軟件危機而產生的,這是怎麼回事呢?

卡內基·梅隆大學的瑪麗·肖(Mary Shaw)和戴維·加蘭(David Garlan)對軟件架構做了很多研究,他們在 1994 年的一篇文章《軟件架構介紹》(An Introduction to Software Architecture)中寫到:

“When systems are constructed from many components, the organization of the overall system-the software architecture-presents a new set of design problems.”

簡單翻譯一下:隨着軟件系統規模的增加,計算相關的算法和數據結構不再構成主要的設計問題;當系統由許多部分組成時,整個系統的組織,也就是所說的“軟件架構”,導致了一系列新的設計問題。

這段話很好地解釋了“軟件架構”爲何先在 Rational 或者 Microsoft 這樣的大公司開始逐步流行起來。因爲只有大公司開發的軟件系統才具備較大規模,而只有規模較大的軟件系統纔會面臨軟件架構相關的問題,例如:

  • 系統規模龐大,內部耦合嚴重,開發效率低;
  • 系統耦合嚴重,牽一髮動全身,後續修改和擴展困難;
  • 系統邏輯複雜,容易出問題,出問題後很難排查和修復。

軟件架構的出現有其歷史必然性。20 世紀 60 年代第一次軟件危機引出了“結構化編程”,創造了“模塊”概念;20 世紀 80 年代第二次軟件危機引出了“面向對象編程”,創造了“對象”概念;到了 20 世紀 90 年代“軟件架構”開始流行,創造了“組件”概念。我們可以看到,“模塊”“對象”“組件”本質上都是對達到一定規模的軟件進行拆分,差別只是在於隨着軟件的複雜度不斷增加,拆分的粒度越來越粗,拆分的層次越來越高。

二、架構概念

先梳理幾個有關係而又相似的概念

2.1 系統與子系統

我們先來看維基百科定義的“系統”。

系統泛指由一羣有關聯的個體組成,根據某種規則運作,能完成個別元件不能單獨完成的工作的羣體。它的意思是“總體”“整體”或“聯盟”。

我來提煉一下里面的關鍵內容:
關聯:系統是由一羣有關聯的個體組成的,沒有關聯的個體堆在一起不能成爲一個系統。例如,把一個發動機和一臺 PC 放在一起不能稱之爲一個系統,把發動機、底盤、輪胎、車架組合起來才能成爲一臺汽車。

規則:系統內的個體需要按照指定的規則運作,而不是單個個體各自爲政。規則規定了系統內個體分工和協作的方式。例如,汽車發動機負責產生動力,然後通過變速器和傳動軸,將動力輸出到車輪上,從而驅動汽車前進。

能力:系統能力與個體能力有本質的差別,系統能力不是個體能力之和,而是產生了新的能力。例如,汽車能夠載重前進,而發動機、變速器、傳動軸、車輪本身都不具備這樣的能力。

我們再來看子系統的定義。

子系統也是由一羣有關聯的個體所組成的系統,多半會是更大系統中的一部分。

其實子系統的定義和系統定義是一樣的,只是觀察的角度有差異,一個系統可能是另外一個更大系統的子系統。

按照這個定義,系統和子系統比較容易理解。我們以微信爲例來做一個分析。

  1. 微信本身是一個系統,包含聊天、登錄、支付、朋友圈等子系統。
  2. 朋友圈這個系統又包括動態、評論、點贊等子系統。
  3. 評論這個系統可能又包括防刷子系統、審覈子系統、發佈子系統、存儲子系統。
  4. 評論審覈子系統不再包含業務意義上的子系統,而是包括各個模塊或者組件,這些模塊或者組件本身也是另外一個維度上的系統。例如,MySQL、Redis等是存儲系統,但不是業務子系統。

2.2 模塊與組件

模塊和組件兩個概念在實際工作中很容易混淆,我們經常能夠聽到類似這樣的說法:

  • MySQL 模塊主要負責存儲數據,而 ElasticSearch 模塊主要負責數據搜索。
  • 我們有安全加密組件、有審覈組件。
  • App的下載模塊使用了第三方的組件。

造成這種現象的主要原因是,模塊與組件的定義並不好理解,也不能很好地進行區分。我們來看看這兩者在維基百科上的定義。

軟件模塊(Module)是一套一致而互相有緊密關連的軟件組織。它分別包含了程序和數據結構兩部分。現代軟件開發往往利用模塊作爲合成的單位。模塊的接口表達了由該模塊提供的功能和調用它時所需的元素。模塊是可能分開被編寫的單位。這使它們可再用和允許人員同時協作、編寫及研究不同的模塊。軟件組件定義爲自包含的、可編程的、可重用的、與語言無關的軟件單元,軟件組件可以很容易被用於組裝應用程序中。

可能你看完這兩個定義後一頭霧水,還是不知道這兩者有什麼區別。造成這種現象的根本原因是,模塊和組件都是系統的組成部分,只是從不同的角度拆分系統而已。

從邏輯的角度來拆分系統後,得到的單元就是“模塊”;從物理的角度來拆分系統後,得到的單元就是“組件”。劃分模塊的主要目的是職責分離;劃分組件的主要目的是單元複用。其實,“組件”的英文 component 也可翻譯成中文的“零件”一詞,“零件”更容易理解一些,“零件”是一個物理的概念,並且具備“獨立且可替換”的特點。

我以一個最簡單的網站系統來爲例。假設我們要做一個學生信息管理系統,這個系統從邏輯的角度來拆分,可以分爲“登錄註冊模塊”“個人信息模塊”“個人成績模塊”;從物理的角度來拆分,可以拆分爲 Nginx、Web 服務器、MySQL。

2.3 框架與架構

框架是和架構比較相似的概念,且兩者有較強的關聯關係,所以在實際工作中,這兩個概念有時我們容易分不清楚。參考維基百科上框架與架構的定義,我來解釋兩者的區別。

軟件框架(Software framework)通常指的是爲了實現某個業界標準或完成特定基本任務的軟件組件規範,也指爲了實現某個軟件組件規範時,提供規範所要求之基礎功能的軟件產品。

我來提煉一下其中關鍵部分:

  1. 框架是組件規範:例如,MVC 就是一種最常見的開發規範,類似的還有 MVP、MVVM、J2EE 等框架。
  2. 框架提供基礎功能的產品:例如,Spring MVC 是 MVC 的開發框架,除了滿足 MVC 的規範,Spring 提供了很多基礎功能來幫助我們實現功能,包括註解(@Controller 等)、Spring Security、Spring JPA 等很多基礎功能。

軟件架構指軟件系統的“基礎結構”,創造這些基礎結構的準則,以及對這些結構的描述。

單純從定義的角度來看,框架和架構的區別還是比較明顯的,框架關注的是“規範”,架構關注的是“結構”。框架的英文是 Framework,架構的英文是 Architecture。Spring MVC 的英文文檔標題就是“Web MVC framework”。

雖然如此,在實際工作中我們卻經常碰到一些似是而非的說法。例如,“我們的系統是 MVC 架構”“我們需要將 android app 重構爲 MVP 架構”“我們的系統基於 SSH 框架開發”“我們是 SSH 的架構”“XX 系統是基於 Spring MVC 框架開發,標準的 MVC 架構”……

究竟什麼說法是對的,什麼說法是錯的呢?其實這些說法都是對的,造成這種現象的根本原因隱藏於架構的定義中,關鍵就是“基礎結構”這個概念並沒有明確說是從什麼角度來分解的。採用不同的角度或者維度,可以將系統劃分爲不同的結構。以“學生管理系統”爲例。
從業務邏輯的角度分解,“學生管理系統”的架構是:
在這裏插入圖片描述
從物理部署的角度分解,“學生管理系統”的架構是:

在這裏插入圖片描述
從開發規範的角度分解,“學生管理系統”可以採用標準的 MVC 框架來開發,因此架構又變成了 MVC 架構:
在這裏插入圖片描述
這些“架構”,都是“學生管理系統”正確的架構,只是從不同的角度來分解而已,這也是 IBM 的 RUP 將軟件架構視圖分爲著名的“4+1 視圖”的原因。

2.4 什麼是架構

參考維基百科的定義,我將架構重新定義爲:軟件架構指軟件系統的頂層結構。

這個定義看似很簡單,但包含的信息很豐富,基本上把系統、子系統、模塊、組件、架構等概念都串起來了,我來詳細解釋一下。

首先,“系統是一羣關聯個體組成”,這些“個體”可以是“子系統”“模塊”“組件”等;架構需要明確系統包含哪些“個體”。

其次,系統中的個體需要“根據某種規則”運作,架構需要明確個體運作和協作的規則。

第三,維基百科定義的架構用到了“基礎結構”這個說法,我改爲“頂層結構”,可以更好地區分系統和子系統,避免將系統架構和子系統架構混淆在一起導致架構層次混亂。

三、架構設計的目的

3.1 架構設計的幾個誤區

關於架構設計的目的,常見的誤區有:

  • 因爲架構很重要,所以要做架構設計

這是一句正確的廢話,架構是很重要,但架構爲何重要呢?

例如:不做架構設計系統就跑不起來麼?

其實不然,很多朋友尤其是經歷了創業公司的朋友可能會發現,公司的初始產品可能沒有架構設計,大夥擼起袖子簡單討論一下就開始編碼了,根本沒有正規的架構設計過程,而且也許產品開發速度還更快,上線後運行也還不錯。

例如:做了架構設計就能提升開發效率麼?

也不盡然,實際上有時候最簡單的設計開發效率反而是最高的,架構設計畢竟需要投入時間和人力,這部分投入如果用來儘早編碼,項目也許會更快。

例如:設計良好的架構能促進業務發展麼?

好像有一定的道理,例如設計高性能的架構能夠讓用戶體驗更好,但反過來想,我們照抄微信的架構,業務就能達到微信的量級麼?肯定不可能,不要說達到微信的量級,達到微信的 1/10 做夢都要笑醒了。

  • 不是每個系統都要做架構設計嗎

這其實是知其然不知其所以然,系統確實要做架構設計,但還是不知道爲何要做架構設計,反正大家都要做架構設計,所以做架構設計肯定沒錯。

這樣的架構師或者設計師很容易走入生搬硬套業界其他公司已有架構的歧路,美其名曰“參考”“微改進”。一旦強行引入其他公司架構後,很可能會發現架構水土不服,或者運行起來很彆扭等各種情況,最後往往不得不削足適履,或者不斷重構,甚至無奈推倒重來。

  • 公司流程要求系統開發過程中必須有架構設計

與此答案類似還有因爲“架構師總要做點事情”,所以要做架構設計,其實都是捨本逐末。因爲流程有規定,所以要做架構設計;因爲架構師要做事,所以要做架構設計,這都是很表面地看問題,並沒有真正理解爲何要做架構設計,而且很多需求並不一定要進行架構設計。如果認爲架構師一定要找點事做,流程一定要進行架構設計,就會出現事實上不需要架構設計但形式上卻繼續去做架構設計,不但浪費時間和人力,還會拖慢整體的開發進度。

  • 爲了高性能、高可用、可擴展,所以要做架構設計

能夠給出這個答案,說明已經有了一定的架構經歷或者基礎,畢竟確實很多架構設計都是衝着高性能、高可用……等“高 XX”的目標去的。

但往往持有這類觀點的架構師和設計師會給項目帶來巨大的災難,這絕不是危言聳聽,而是很多實際發生的事情,爲什麼會這樣呢?因爲這類架構師或者設計師不管三七二十一,不管什麼系統,也不管什麼業務,上來就要求“高性能、高可用、高擴展”,結果就會出現架構設計複雜無比

3.2 架構設計的目的

整個軟件技術發展的歷史,其實就是一部與“複雜度”鬥爭的歷史,架構的出現也不例外。簡而言之,架構也是爲了應對軟件系統複雜度而提出的一個解決方案,通過回顧架構產生的歷史背景和原因,架構設計的主要目的是爲了解決軟件系統複雜度帶來的問題

這個結論雖然很簡潔,但卻是架構設計過程中需要時刻銘記在心的一條準則,爲什麼這樣說呢?

首先,遵循這條準則能夠讓“新手”架構師心中有數,而不是一頭霧水。如從哪裏下手,時間緊,到底用哪種架構等問題,弄清楚我們需要解決和最關鍵的是要解決那種複雜度。

其次,遵循這條準則能夠讓“老鳥”架構師有的放矢,而不是貪大求全。l老鳥爲了證明自己的技術牛,可能會陷入貪大求全的焦油坑而無法自拔。弄清楚引入這些技術是減少了我們的複雜度,還是另一個角度上增加了我們的複雜度,是不是我們真正需要解決的複雜度。

四、寫在最後

本文絕大部分內容出自極客時間-李運華老師的 從0開始學架構.,如有條件歡迎支持購買正版。

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