天天看點

吃透MVC,馴服爛代碼 吃透MVC,馴服爛代碼

吃透MVC,馴服爛代碼

吃透MVC,馴服爛代碼 吃透MVC,馴服爛代碼

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

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

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

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

吃透MVC,馴服爛代碼 吃透MVC,馴服爛代碼

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

頭破血流後,意識到代碼複用的重要性

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

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

改進後的僞代碼:

吃透MVC,馴服爛代碼 吃透MVC,馴服爛代碼

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

原來還有代碼職責這回事

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

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

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

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

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

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

改進後僞代碼:

吃透MVC,馴服爛代碼 吃透MVC,馴服爛代碼

摸爬滾打中和 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,馴服爛代碼 吃透MVC,馴服爛代碼

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

為什麼要用 MVC

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

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

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

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

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

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