爲什麼要做代碼分層架構?

來源:http://dwz.date/eNE3

軟件程序通常有兩個層面的需求:

  • 功能性需求,簡單來說,就是一個程序能爲用戶做些什麼,比如,文件上傳、查詢數據等;
  • 非功能性需求,這個是指除功能性需求以外的其他必要需求,比如,性能、安全性、容錯與恢復、本地化、國際化等。

事實上,非功能性需求所構建起來的正是我們所熟知的軟件架構。什麼是軟件架構?簡單來說,就是軟件的基本結構,包括三要素:代碼、代碼之間的關係和兩者各自的屬性。

我們都知道,軟件架構非常重要,爲什麼重要呢?如果把軟件比作一座高樓,那麼軟件架構就是那個鋼筋混凝土的框架,代碼就是那個框架裏的磚石,正是因爲有了那個框架,才能讓每一個代碼都能很好地運行起來。

其中,最爲經典的軟件架構就是分層架構,也就是將軟件系統進行分層,現在幾乎已經成爲每個程序員最熟悉的思考模式之一。不過,分層架構越是流行,我們的設計越容易僵化。這背後到底有哪些值得我們深思的地方呢?

所以,今天我就從架構角度來聊聊爲什麼代碼要做分層、主要用於解決什麼問題,以及存在優勢和劣勢有哪些。

代碼分層架構是什麼

要想徹底理解 代碼分層架構,就得從軟件部署分層架構說起。首先我們來看一下常見的互聯網軟件部署分層架構,如下圖所示:

由圖可以看到,軟件部署分層架構主要包括以下四個核心部分。

  • 客戶端層(Client):調用方,比如瀏覽器或 App。
  • 應用服務層的網頁服務器(Web Server):實現程序的運行邏輯,並從下層獲取數據,返回給上層的客戶端層。
  • 應用服務層的緩存(Cache):加速訪問存儲的數據。
  • 數據層(DB):存儲數據。

通過上面的分析,現在你應該知道什麼是軟件分層架構了吧?軟件分層架構是通過層來隔離不同的關注點(變化相似的地方),以此來解決不同需求變化的問題,使得這種變化可以被控制在一個層裏。

作爲軟件開發者,我們更關心的其實是應用程序裏的分層架構。比如,下圖展示的現在流行的一種 MVC 分層架構:

我們能明顯看到,MVC 分層架構是作用於程序本身的,程序作爲一個整體被髮布在服務器上運行使用。而類似 DB 裏也有自己的分層架構,這裏我們重點介紹應用程序中的代碼分層架構,其他架構就不展開討論了。

那麼問題來了,什麼是代碼分層架構呢?

代碼分層架構就是將軟件“元素”(代碼)按照“層”(代碼關係)的方式組織起來的一種結構。

分層架構核心的原則是:當請求或數據從外部傳遞過來後,必須是從上一層傳遞給下一層。如下圖,一個來自 View 層的數據,必須先通過 Controller 層、Model 層後,才能最終到達數據庫層。

那麼你可能會問:“爲什麼不讓 View 層的請求直接到達數據庫呢?”
這是因爲會造成 新的代碼耦合,增加代碼的複雜度
比如說,View 層直接調用 Model 層的組件,當 Model 層上的組件有變化時(比如, SQL 或邏輯修改),既會影響 Controller 層組件的使用,也會影響 View 層組件的使用(可參考下面的示意圖)。

所以,分層的本質就是爲了讓相似變化在各自的層內變化,而不造成層與層之間的相互影響。

代碼分層架構解決什麼問題

代碼分層架構主要是爲了解決兩個問題:

  • 如何快速拆解功能問題?
  • 如何提升代碼的可擴展性?

下面我們就來分別解釋下。

1. 通過分層來拆解問題

在軟件開發中,一個功能需求問題通常都是籠統的複雜問題,我們一般都會將這個籠統的複雜問題拆分爲多個層次的子問題來解決。

這裏來看一個簡單的例子,假定你正在編寫一段“通過 HTTP 向服務器發送字符串”的代碼,如下所示:

//創建HTTP連接
URL url = new URL("http://xxx.test.com/sayHello");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.connect();
//發送數據
OutputStream os = connection.getOutputStream();
os.write("Hello World!".getBytes("UTF-8"));
//接收響應
InputStream is = connection.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
//……
br.close();
is.close();
os.close();
//關閉連接
connection.disconnect();

我們將這段代碼簡單地抽象成一個流程圖,如下所示:


這個流程圖代表了我們對最初始問題的分層拆分:先創建 HTTP 連接,然後向服務器發送一串字符串,最後關閉 HTTP 連接。

於是,原先的“如何通過 HTTP 向服務器發送字符串”的問題就變成了三個新層次的子問題:

  • 如何創建 HTTP 連接?
  • 如何發送字符串?
  • 如何關閉連接?

首先,在思考如何創建 HTTP 連接這個問題的過程中,你會發現,要想通過 HTTP 發送消息,至少得打開 HTTP 連接,建立 HTTP 會話,並使用 TCP 協議,這樣才能通過網絡發送數據。

接着,你又發現,當成功解決了這個問題後,發送字符串和關閉 HTTP 連接還有更多的問題需要解決,於是,你開始一步一步地去分解……最後的分解結果如下圖所示:

當所有子問題都被成功解決以後,最初通過 HTTP 向服務器發送字符串的總問題也就得以解決了。

你發現沒有,在不知不覺中你就通過分層將一個複雜的大問題分解爲多個容易解決的子層問題。而實際上,有的子層問題已經被前人解決過了,比如,如何使用 HTTP 協議來進行網絡數據的通信。也就是說,最後真正需要關注的問題其實變少了

所以說,從功能性需求角度來看,代碼分層本身就是一種拆解複雜問題的好方法。

2. 通過分層來提升代碼可擴展性

分層架構的出現,除了解決拆分複雜問題的困境外,還解決了代碼可擴展性的問題。

爲什麼要提升代碼可擴展性?因爲真實的系統數據一直在不斷增加。比如說,一個電商網站的用戶訪問數會從一萬個併發增長到十萬個併發,或者從一百萬增長到一千萬。過去的單體架構之所以很難承載,是因爲當我們需要擴展服務器和數據庫功能時,一處的代碼修改就會影響所有的功能。

分層架構可以將複雜的邏輯切分爲多個層,這樣大問題就變成了多個小問題,而我們可以很方便地解決每個小問題。每個小問題更容易被抽象爲一個組件,當組件功能需要擴充或替換時,修改代碼的影響也被有效地控制在有限的範圍內,這樣組件自身的複用性也就提高了

除了提高代碼組件之間的複用性外,分層架構還讓我們更容易做服務的橫向擴展

什麼是橫向擴展?簡單來說,就是用多臺配置較低的服務器共同提供服務,也就是我們熟知的集羣部署服務方式。比如說,將 Model 層抽取出來作爲通用的數據服務部署,這樣既不影響其他業務層,也能在負載增加時,快速擴展服務的承載能力。

代碼分層架構的優勢和劣勢

到這裏,代碼分層結構的優勢體現在哪兒就很清楚了,大致可總結爲如下:

  • 只用關注整個結構中的其中某一層的具體實現;
  • 降低層與層之間的依賴;
  • 很容易用新的實現來替換原有層次的實現;
  • 有利於標準化的統一;
  • 各層邏輯方便複用。

總結來說,代碼分層架構設計主要爲了實現責任分離、解耦、組件複用和標準制定

如果不使用分層架構的話,我們的代碼邏輯一定會緊緊依賴在一起,修改某一處必定影響其他很多處。從軟件項目的角度看,這樣會造成非常嚴重的影響。比如,一個上傳功能需要存入下載鏈接到數據庫,如果沒有分層,那麼當修改存儲的路徑或類型時,還得修改存儲數據庫的業務邏輯,想想就很麻煩。

另外,層與層之間進行劃分後,也提高了組件之間的複用性,層本身就是一種組件形式,通過統一的接口來與外界進行交互,而不再是按照功能上的依賴來進行交互。而統一的接口是模塊之間相互約定的統一標準,只要按照標準來進行代碼實現,就不會因爲代碼改動而影響接口的使用。

雖然代碼分層有很多好處,但不可避免地也會有一些劣勢。

  • 開發成本變高:因爲不同層分別承擔各自的責任,如果是高層次新增功能,則需要多個低層增加代碼,這樣難免會增加開發成本。
  • 性能降低:請求數據因爲經過多層代碼的處理,執行時長加長,性能會有所消耗。
  • 代碼複雜度增加:因爲層與層之間存在強耦合,所以對於一些組合功能的調用,則需要增加很多層之間的調用。

總結

軟件分層架構是通過層來隔離不同的關注點(變化相似的地方),以此來解決不同需求變化的問題,使得這種變化可以被控制在一個層裏。

代碼分層架構的核心作用有兩個:

  • 對於功能性需求,將複雜問題分解爲多個容易解決的子層問題;
  • 對於非功能性需求,可以提升代碼可擴展性。

總結來說,代碼分層架構是一種軟件架構設計方法。

  • 從軟件的功能性需求角度看,分層是爲了把較大的複雜問題拆分爲多個較小的問題,在分散問題風險的同時,讓問題更容易被解決,也就是我們常說的解耦。
  • 從架構(非功能性需求)角度看,分層能提升代碼可擴展性,幫助開發人員在相似的變化中修改代碼。

其實,複雜的設計概念和簡單的代碼之間存在一種平衡,這就是分層架構。

  • 代碼分層架構設計的思維模型是簡化思維,本質是抽象與拆解。
  • 代碼分層架構設計的目的是將複雜問題拆分爲更容易解決的小問題,降低實現難度。
  • 代碼分層架構設計的原則和方法是通用方法,可以應用到其他需要分層設計的地方。

所以,分層架構從來不是目的,只是讓我們的軟件變得更好的其中一種思維方法而已。


如果此文對你有所幫助,希望能隨手點個轉發!



End




乾貨分享



這裏爲大家準備了一份小小的禮物,關注公衆號,輸入如下代碼,即可獲得百度網盤地址,無套路領取!

001:《程序員必讀書籍》
002:《從無到有搭建中小型互聯網公司後臺服務架構與運維架構》
003:《互聯網企業高併發解決方案》
004:《互聯網架構教學視頻》
006:《SpringBoot實現點餐系統》
007:《SpringSecurity實戰視頻》
008:《Hadoop實戰教學視頻》
009:《騰訊2019Techo開發者大會PPT》

010: 微信交流羣


本文分享自微信公衆號 - JAVA日知錄(javadaily)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

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