
一 背景和問題
我個人平時會比較慎用“架構”這個詞
一方面是覺得業(yè)界有很多架構大師和架構模式,而我的認知和實踐有限;
另一方面是因為這個詞看著挺高大上、有點務虛,如果不結合實際場景的具體問題來討論,容易陷入“PHP是最好的語言”這樣的辯論賽中。而不同場景中又有各自的問題,程序員們通過自己的理解和思考、針對實際場景對一些架構模式進行了擴展實踐,以此來解決遇到的問題,也會基于同一個模式延伸出一些派生概念。
兵無常勢,水無常形。所以,我個人的觀點是:以要解決的問題為出發(fā)點,去討論我們要采用的架構模式(技術方案)。
另外,由于我們是站在很多巨人肩膀上的,討論時可以站在一些如SOLID等軟件設計/開發(fā)原則的基礎上。
寫這篇文章,我也是從解決一些問題的目的出發(fā)的:
最近和團隊同學討論了相關話題,雖然大多數(shù)同學在實踐上基本一致,但具體到話術、名詞概念和具體使用的理解和實踐上有些差異(這是很正常的,因為業(yè)界對同一個模式的理解和實踐也不同)。我結合一些實際編碼場景做了一番陳述,為了避免后續(xù)重復大費口舌,所以打算寫下來,以后有需要直接發(fā)文章鏈接。
由于我個人的認知和實踐有限,所以也希望能拋(huan)磚(ying)引(lai)玉(pen),讓我學到更多。
雖然同一個架構模式在不同業(yè)務/技術領域的實施會有區(qū)別,但同一個團隊內(nèi)應該保持一致性,因為這樣有助于日常的code review、功能模塊的交接backup等活動,尤其是有利于使用統(tǒng)一的單測建設方案來保障我們的產(chǎn)品質(zhì)量。
實際問題:我最近在開發(fā)商家合并發(fā)貨的功能,但由于之前基礎發(fā)貨功能的界面和邏輯并不是我開發(fā)的,所以我在修改原有代碼、支持有非常多細節(jié)邏輯的合并發(fā)貨能力時,就在擔心對原有發(fā)(zhong)貨(yao)能力的影響。而這時候,如果有單測的保障,我就可以更放心地進行功能升級改造了 —— 別說更復雜的合并發(fā)貨能力了,而這類訴求在復雜的交易場景里很普遍。
提煉一下我遇到的具體問題:
在由不同開發(fā)人員持續(xù)迭代、進行功能升級的軟件開發(fā)活動中,如何保障具有復雜邏輯的商家經(jīng)營工具的產(chǎn)品質(zhì)量。
軟件開發(fā)活動是整個流程的核心環(huán)節(jié):接收產(chǎn)品和視覺設計需求/變更作為輸入,然后輸出客戶可用的終端產(chǎn)品。
而統(tǒng)一的軟件開發(fā)架構模式,則是我們保障軟件開發(fā)質(zhì)量的基礎。(這里就不具體展開WHY了)
由于討論的是具體面向客戶使用的業(yè)務場景,少不了客戶操作交互的視圖層(View),所以我從MVC開始談起。
二 從表現(xiàn)層的MVC談起
雖然我平時比較慎用“架構”這個詞,但我平時喜歡隨手拍一些建筑物。因為建筑之美,會讓我聯(lián)想到軟件的架構也應該有美感,畢竟Software Architecture這個概念也是起源于Architecture。
這時候,架構這個詞就會給我一種接地氣的感覺:有多少塊磚,每塊磚做什么用、放到哪里去,這塊磚 和 那塊磚怎么黏在一起或互相支撐。當然,由于軟件的可移植性、可復用性,從某些角度來講,軟件架構相比建筑架構有其更復雜的地方。
MVC誕生至今已經(jīng)超過40年了(Since 1979),10多年前就得到過很廣泛的討論和實踐,穿越時空到今天肯定有其反脆弱性和內(nèi)在核心價值。雖然如今乍看起來好像已經(jīng)過氣、被討論過千百遍了,但仍然有很多程序員會有不同理解和看法,或多或少。這是很正常的,上面也提到了部分原因,這里具體再展開下。
1 MVC在經(jīng)典三層架構里的位置
MVC是一種通用架構模式
早期PC時代應用在桌面客戶端,
后來在Web時代變得流行(我以前寫PHP也用過相關MVC框架),
如今在移動互聯(lián)網(wǎng)時代也得到廣泛應用。
上面這三個場景的應用,都是面向客戶的,需要交互表現(xiàn)的。
從MVC命名中的View(視圖)也可以看出,MVC模式應用在軟件系統(tǒng)架構里的表現(xiàn)層。
在業(yè)界某知名公司的官方文檔里,也明確把MVC放在Web Presentation Patterns下。
我之所以沒有在上圖中對M-V-C添加箭頭線條,是因為在這一點上,不同程序員也有不同理解和實踐。
這是第一個需要明確的點:MVC架構模式在多層系統(tǒng)架構里的應用范圍。
左側 業(yè)務表現(xiàn)層-業(yè)務服務層-基礎服務層 是移動端三層架構模式,未涉及到 C/S 交互;右側是Web B/S場景的三層架構模式。
因為有些應用會比較簡單,根本不需要業(yè)務服務或基礎服務層,純粹靠一個MVC(或者VC)就能交付出一個Mobile/Web App;
而且在一些業(yè)務系統(tǒng)里,Web前端/桌面客戶端/移動App 也可能會被簡化為 大前端/大終端表現(xiàn)層;
所以可能基于不同信息,不同程序員對此會有不同認知。
但隨著用戶終端應用的重要性和復雜度的提升,已經(jīng)從簡單應用發(fā)展到復雜多團隊協(xié)同的平臺型或航母級應用,僅靠一個MVC來完成交付是不合適的。
我們也可以反過來想,程序員會把以下代碼放在客戶端代碼的哪一層:
對Web引擎的擴展邏輯。
通信協(xié)議的結構定義,以及相應的socket連接和通信代碼。
一個業(yè)務相關且UI無關的平臺開放能力。
Crash捕獲、卡頓監(jiān)控、日志埋點等功能實現(xiàn),比如Android在做APM相關事情時會采用AOP方式,利用ASM、AspectJ等方案來做字節(jié)碼插樁。
……
2 業(yè)界基于MVC模式的不同實踐
前面提到不同程序員對MVC模式的理解和實踐存在差異
業(yè)界大廠亦然,以下會結合業(yè)界一些知名且有影響力的公司在MVC模式上的實踐,做進一步的展開討論。
知名公司A
知名公司A在指導開發(fā)者使用MVC時,推薦下圖方式:
可以看出在他們的實踐上:
Controller可以引用View和Model。
View可以引用Model。
這里的Model傾向于是Passive。
同時,他們建議:
在強類型視圖場景,控制器從模型創(chuàng)建并填充ViewModel實例,該ViewModel 實例包含要在該視圖上顯示的數(shù)據(jù)。
當控制器由于責任過多而變得過于復雜時,也就是業(yè)界戲稱的“MVC means Massive View Controller”,需要將業(yè)務邏輯從控制器移出并推入域模型中。
知名公司B
說到Massive View Controller,知名公司B在移動互聯(lián)網(wǎng)方興未艾的時候,推薦下圖所示的MVC實踐方案:
上圖呈現(xiàn)出:
Controller引用View和Model。
Model通過一些松耦合方式來觸達Controller,如廣播通知、callback等,驅動Controller做出響應。
View通過代理模式等方案弱依賴Controller,由Controller對各種用戶操作、UI渲染訴求做出響應。
而View和Model之間是隔離的,Model變化后對View的更新操作全部由Controller負責。
不過相應的官方文檔已經(jīng)被聲明是過期文檔了,并備注不一定是目前的最佳實踐。
是的,隨著移動互聯(lián)網(wǎng)蓬勃發(fā)展,十年前的“最佳實踐”被一路多種挑戰(zhàn) —— 在采用這種方案的開發(fā)領域中,如何重構Massive View Controller為Lighter View Controller已經(jīng)成為了一個專題。
對比和思考
A和B的異同點
相同點:Model包含 所需的數(shù)據(jù)結構封裝,以及相應數(shù)據(jù)操作的方法定義。即Data + 本地或遠端的CURD。
差異點:在知名公司A給的圖中,View可以引用Model,而在知名公司B給的圖中則不行。
一些問題和思考
View有箭頭指向Model,這里的引用關系是指什么?是View持有Model.Data數(shù)據(jù)對象,還是View調(diào)用Model.CURD方法。
Controller的本意是Controing Logic,那除了ViewController外,是否還可以有其它的XxController,比如DataSourceController、NotificationController?
從命名上看,既然ViewController 既有View 又有Controller,那為什么把它放在 C里面,而不放在V里面呢?比如當我們在iOS/Android開發(fā)中引入MVVM模式后,ViewController或Activity屬于M-VM-V的哪部分呢,代碼放在哪個目錄下呢?
我有類名使用ViewModel后綴就代表我使用MVVM模式了嗎?
Martin Fowler
作為
《重構 : 改善既有代碼的設計》、《企業(yè)應用架構模式》等著作的作者;
敏捷軟件開發(fā)宣言創(chuàng)作者之一;
MVVM模式誕生時參考引用的技術專家。
Martin Fowler給的MVC模式圖如下:
和上面知名公司A和B的圖,又不一樣了,不過他這里也是認為View可以引用Model的。
MVC和DDD
Martin Fowler和《領域驅動設計》作者Eric Evans也討論過MVC中Model的設計理念:
貧血模型:將Model分為簡(pin)單(xue)的Model數(shù)據(jù)對象,和處理操作數(shù)據(jù)對象的Service/Manager/BizLogic等。
示例:為aPerson修改name,則由 CitizenService.changeNameOfPerson( aPerson ) 這種方式來實現(xiàn)。
充血模型:將對應領域的處理邏輯放到領域模型中,使得這個領域模型更飽(chong)滿(xue)。
示例:aPerson要刷牙,則由 aPerson.brushTeeth() 來實現(xiàn)。
補充:充血模型更有面向對象編程的味道,尤其是搭配交易領域等業(yè)務場景,更有體感。不過稍微細想一下,可能就會發(fā)現(xiàn)DDD對設計的要求會更高,從而對研發(fā)周期和質(zhì)量保障提出了新的要求,并且可能引起對現(xiàn)有系統(tǒng)的大規(guī)模重構。(盒馬的DDD實踐)
也就是說,大到MVC各個模塊的依賴引用關系,細到Model中的代碼設計方式,業(yè)界都有不同的理念和實踐。
Java Web開發(fā)領域也對Model的設計產(chǎn)生過非常激烈的討論。
小結
先拋開具體模塊的代碼設計方案,基于以上幾種業(yè)界大廠或專家的描述,我小結了以下這張圖并標注了待解問題:
問題一:如何解決MVC中Controller的膨脹臃腫問題?
要回答如何解決,需要先思考為什么膨脹。
問題二:View能否引用Model?
要回答能否引用,需要先定義引用關系是什么。
是持有對象,還是調(diào)用CURD接口操作對象。
又或者這兩者沒有必要區(qū)分,因為持有的對象本身就可能帶CURD接口。
參考上面相關資料,目前業(yè)界有的支持、有的反對。
問題三:存在View -> Model,那么是否可以反過來存在 Model -> View?
和問題二在描述上相反但又有關聯(lián),如果對問題再進一步提問的話:
使用 -> 引用關系,是為了解決什么問題?
使用 -> 引用關系,會產(chǎn)生什么問題?
如同文章開頭所說,以上問題需要結合具體場景來展開(見 實際案例結合),盡量從務虛到務實。
本文由培訓無憂網(wǎng)長沙牛耳教育課程顧問老師整理發(fā)布,希望能夠對想在長沙參加影視動漫培訓的學生有所幫助。更多課程信息可關注培訓無憂網(wǎng)電腦IT培訓頻道或添加老師微信:15033336050
注:尊重原創(chuàng)文章,轉載請注明出處和鏈接 http://m.elsolbar.com/news-id-13948.html 違者必究!部分文章來源于網(wǎng)絡由培訓無憂網(wǎng)編輯部人員整理發(fā)布,內(nèi)容真實性請自行核實或聯(lián)系我們,了解更多相關資訊請關注程序開發(fā)頻道查看更多,了解相關專業(yè)課程信息您可在線咨詢也可免費申請試課。關注官方微信了解更多:150 3333 6050