由淺入深吃透MVC框架,馴服爛代碼

jopen 8年前發布 | 26K 次閱讀 MVC模式 Web框架

MVC 已經成為客戶端的主流編程框架,相信客戶端工程師對它并不陌生,甚至在開發過程中,不通過思考都會自動使用 MVC 框架編程。但在工作過程中,發現許多小伙伴也只是使用 MVC,對于為什么這樣使用并不清楚。本篇文章將由淺入深,一步一步解釋為什么要使用 MVC,以及使用 MVC 所帶來的好處。

剛開始工作時,我是這樣快速完成任務的

在剛開始工作時,拿到一個任務,第一時間想到的是怎么將它快速實現,但從未想過怎么將它做好,代碼從頭到尾都在文件里,一氣呵成,速度比別人要快一倍,然后坐在那里,靜靜的等待著下一個任務的到來,有時還會偷偷的鄙視一下比我做得慢的同事,心中竊喜。但隨著項目慢慢變大,問題就來了,一個 Controller 里的代碼有時候會變成上千行,甚至幾千行,代碼的可讀性變得非常差,存在大量重復代碼,項目的一個小變更經常無法定位代碼。經常干的事情就是全局搜索,替換相同的代碼,一不小心改錯一個,半天的工作得從頭開始。

當年代碼是這樣的(偽代碼):

這是一段簡短的代碼示例,但確能看出很多的問題。API 未封裝、無統一網絡接口、數據沒模型、無任何編程思想等。

頭破血流后,意識到代碼復用的重要性

項目漸漸變大,編碼幾乎無法繼續下去,面對需求變更,除了頭疼,還是頭疼。都說人感到痛苦的時候就是在成長,說得沒錯,這是我的第一次成長。在完成工作任務的空閑時間里,我開始試著重構自己的代碼。我知道我所面對的直接問題是重復代碼太多,需要代碼復用,但對于工作經驗不足的我,首先想到復用方法是函數。把網絡請求、數據解析、數據存儲、邏輯處理、頁面刷新等都封裝成了函數,代碼結構瞬間變得清晰起來,Controller 里面代碼減少 20%,成就感滿滿的。

項目越來越復雜,功能也一直在增加。漸漸發現自己的開發速度又在慢下來,上司對我工作也開始表現出不滿。我知道自己的這種復用機制已經不能應對項目的復雜度。雖然在一個 Controller 里代碼是可以復用了,但不同 Controller 之間的復用都是通過拷貝的,重復代碼還是很多。而且拷貝過去后還要讀懂其他邏輯,代碼耦合度非常高。更糟糕的是,一個網絡請求如果要增加或者減少一個 Header 參數,又得全局搜索,將所有文件都更改一遍,可擴展性極差。

改進后的偽代碼:

雖然改進后代碼還是很糟糕,但是代碼可讀性有了提高,并且同樣接口調用已經不需要寫第二次。

原來還有代碼職責這回事

無論你考慮問題多么認真,限于已有知識的限制,做的事情不可能百分百完美,問題還是會接踵而來。當項目越來越大,工程文件目錄越來越復雜,編寫代碼時變得非常困惑,不知道代碼到底應該寫在哪里,也不知道別人是不是已經曾經實現過類似的功能,到哪里去引用。或者有時候改一個頁面屬性,到處改都不生效。后來才發現在某個角落里,你已經對這個屬性進行了修改。

這就是代碼職責不清晰所造成的。比如在 View 里面只負責頁面創建和刷新、Model 里面只負責數據處理和邏輯處理、Controller 只負責接口調用,連接 View 和 Model,并管理它們的生命周期。如果在 Controller 里面寫了創建頁面的代碼,那么就是破壞了類的單一職責。一個類,只有一個引起它變化的原因。應該只有一個職責。每一個職責都是變化的一個軸線,如果一個類有一個以上的職責,這些職責就 耦合 在了一起。這會導致脆弱的設計。當一個職責發生變化時,可能會影響其它的職責。另外,多個職責耦合在一起,會影響 復用性

職責不清晰就會造成代碼耦合度高,破壞代碼的復用性,并且代碼的維護成本也變高。每一個類有每一個類的職責,每一類類有每一類類的職責,在項目開發前就都規定好,在后面的多人合作開發中就有規可循,編寫對應功能的代碼知道寫在對應目錄上,類似功能代碼可以去固定文件夾中尋找,更改頁面屬性是只需考慮頁面內代碼。做到輕松愉快編程。

最后,終于明白了封裝和繼承的好處

慶幸的是,我的爛代碼在項目演進而蔓延中,但并沒有釀成不可挽回之勢。我及時意識到了問題的嚴重性。

隨著工作經驗的積累,學到的技術越來越多,封裝和繼承在這正好能解決代碼重用度低和可擴展性差的問題。于是我開始第二次重構自己代碼。我寫了個網絡請求的基類,所有網絡請求在這里都有個唯一出口,相同類型的網絡請求我將他封裝在一個類中,并且每個網絡請求都有接口。數據我給它建立對應的模型,并在模型中解析對應數據源,再建立一個類來負責這個模型數據的增刪改查和更新,如果有數據緩存,再創建一個類來管理這個模型的數據存儲。這樣一來,在不同 Controller 中用到同樣的數據請求或模型,只需引用對應的類,調用相應的接口就行,不在需要再去理解相關代碼邏輯,或者看 API 文檔,真正實現了代碼的封裝性和復用性。而且網絡接口類,模型類都有對應基類,代碼的可變更性和擴展性也得到了保障。

改進后偽代碼:

摸爬滾打中和 MVC 第一次相識

第一次相識,并不是指第一次聽說,MVC 一直貫穿編程過程中,只是第一次感覺對它有些理解。做完代碼職責后,就已經大體上遵循了 MVC 編程框架思想。只是上面沒有特別說明將視圖剝離出來。

MVC 全名是 Model View Controller,是模型(model)-視圖(view)-控制器(controller)的縮寫,是一個框架模式。MVC 三部分組成,各司其職,結構清晰明朗。程序的業務邏輯、數據、界面顯示完全分離,將業務邏輯聚集到一個部件里面,在改進和個性化定制界面及用戶交互的同時,不需要重新編寫業務邏輯。

Model(模型)表示應用程序數據,邏輯處理,數據庫記錄列表。模型能為多個視圖提供數據,由于應用于模型的代碼只需寫一次就可以被多個視圖重用,所以減少了代碼的重復性。

View(視圖)是應用程序中處理數據顯示的部分,視圖處于交互層,用于將數據展示給用戶,并傳達用戶的操作指令。

Controller(控制器)是應用程序處理用戶交互的部分,通常控制器負責從視圖讀取數據,控制用戶輸入,并向模型發送數據。

如下圖所示:MVC 中 M 與 V 兩者不存在任何直接交互。存在直接交互的只有 M 和 C 之間與 C  和 V 之間。首先我們來看 M 與 C 的交互,如圖所示,有一條綠色的箭頭從 C 發起指向了 M,代表了 C 與 M 之間的交互應該首先從 Controller 發起,C 首先向 M 提出自己的需求,再由 M 作出響應。C 具有導入 M 頭文件的 API,因此 C 可以知曉 M 的一切內容,如同圖中的那一條白色的虛線。再來看看相對復雜的 C 與 V 之間的交互,這次我們仍然從綠色的箭頭開始了解,箭頭仍然由 C 發起,可見 C與 V 之間的交互依舊由 Controller 發起,但箭頭起始端寫了 outlet 一個詞,意思是“輸出口”,outlet可以看作是從 C 指向 V 的指針,它在 C 中被定義。outlet 給我們提供了很大的方便,它使我們在 C 的內部就可以輕松準確地向 V 施令。C 可以擁有很多的 outlet,可以不止一個,這也使它可以更高效的和V進行交流。

我們以一個頁面展示網絡數據為例,來說明 MVC 之間元素的通信。首先用戶通過 View 發出指令要刷新頁面數據,然后 View 把這個指令通過 outlet 或 delegate 傳達給 Controller,Controller 向 Model 提出要給它數據,Model 完成數據組織后返回給 Controller,Controller 再通過 data source 刷新 View。到這里,一條完整的通信就完成了。

為什么要用 MVC

MVC 的低耦合性、高重用性、可維護性等優點顯而易見,使得原本復雜的代碼與界面的交互變得簡單、清晰、明了。而且 MVC 不同的層各司其職,有利于多人協作開發項目。

在我經歷的幾個工作階段中,第一階段編碼無任何編程思想,所有代碼都混雜在一起,顯得混亂不堪,可讀性非常差。要找一段邏輯,經常無法定位。

第二階段知道用函數來包裝一些重復代碼,使得代碼結構變得清晰。如果說之前編程思想是一頁白紙的話,那么這一過程我開始在這頁白紙上畫了一筆,收獲了面向過程的思想。編程思路漸漸清晰起來。

第三階段開始接觸繼承和封裝這些面向對象的高級特性,真正解決了之前編程過程中所面對的代碼可讀性差、復用性低、可維護性難等問題。如果把編程思想比作畫畫,那么經過這一階段我已經能在白紙上畫出形狀。

第四階段主要學會了和人協作。

而 MVC 的出現就是為了解決我們在工作各階段所面臨的問題,學習 MVC 編程框架,可以讓你不用經歷我所走過的彎路。

-END-

本站內容采用 知識共享署名 4.0 國際許可協議 進行許可。

來自: https://blog.wilddog.com/?p=821

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!