MVC
MVC對于每個從事iOS開發(fā)的人來說都不陌生,MVC架構(gòu)是構(gòu)建iOS應用的標準模板, 作為一種軟件設計模式,MVC這個概念已經(jīng)誕生好多年了娘扩。對于剛剛從事iOS工作 或者相當一段時間的人來說都了解Model-View-Controller.蘋果從最初的發(fā)展初期就引入了這一概念,所以我們不會感覺陌生.
模型-視圖-控制器(Model-View-Controller,MVC)是Xerox PARC在20世紀80年代為編程語言Smalltalk-80發(fā)明的一種軟件設計模式,至今已廣泛應用于用戶交互應用程序中脉执。在iOS開發(fā)中MVC的機制被使用的淋漓盡致,充分理解iOS的MVC模式,有助于我們程序的組織合理性腊瑟。
****模型對象****
模型對象屬于應用程序的數(shù)據(jù)部分,其封裝了應用程序的數(shù)據(jù)以及操縱和處理數(shù)據(jù)的業(yè)務邏輯和運算. 例如用戶在視圖層面所做了導致數(shù)據(jù)變化的操作,就會有中間的控制器來進行傳達,最終表現(xiàn)為模型對象的改變. 當模型對想發(fā)生變化了之后也會相應的經(jīng)過控制器來傳遞,從而達到視圖對象的更新.
****視圖對象****
視圖對象屬于應用層的對象缕溉。視圖對象知道如何將自己繪制出來,并可能對用戶的操作作出響應铡溪。視圖對象的主要目的,就是顯示來自應用程序模型對象的數(shù)據(jù),并使該數(shù)據(jù)可被編輯远荠。盡管如此,在 MVC 應用程序中,視圖對象通常與模型對象分離,他們之間的通訊要經(jīng)過控制器的傳達。
****控制器對象****
視圖層和模型層相互分離, 而在應用程序的一個或多個視圖對象和一個或多個模型對象之間,控制器對象充當媒介作用评雌。控制器對象因此是同步管道程序,通過它,視圖對象了解模型對象的更改,反之亦然,控制器主要負責數(shù)據(jù)的傳遞解耦等工作直焙【岸控制器對象還可以為應用程序執(zhí)行設置和協(xié)調(diào)任務,并管理其他對象的生命周期。
M和V永遠不能相互通信,只能通過控制器傳遞奔誓〗锿拢控制器可以直接與Model對話(讀寫調(diào)用Model),Model通過通知或者KVO機制與控制器間接通信〕梗控制器可以直接與View對話,通過outlet,直接操作View,outlet直接對應到View中的控件,View通過action向控制器報告事件的發(fā)生(如用戶的點擊事件)和措。控制器是View的直接數(shù)據(jù)源(數(shù)據(jù)很可能是控制器從Model中取得并經(jīng)過加工了)蜕煌【势牛控制器是View的代理(delegate),以同步View與Controller。
MVC是一個用來組織代碼的權(quán)威范式,也是構(gòu)建iOS App的標準模式幌绍。Apple甚至是這么說的颁褂。在MVC下,所有的對象被歸類為一個model,一個view,或一個controller。Model持有數(shù)據(jù),View顯示與用戶交互的界面,而View Controller調(diào)解Model和View之間的交互傀广。然而,隨著模塊的迭代我們越來越發(fā)現(xiàn)MVC自身存在著很多不足颁独。因此,MVVM從其他應用而出,在 iOS中從此我們完全將業(yè)務邏輯加以區(qū)分并使用這套思想。
雖然MVC架構(gòu)模式在項目中得到了大家的一直認可,也確實解決很多項目開發(fā)過程中的難題,但是也是存在很多問題的:
愈發(fā)笨重的Controller
在傳統(tǒng)的app中模型數(shù)據(jù)一般都很簡單,不涉及到復雜的業(yè)務數(shù)據(jù)邏輯處理,客戶端開發(fā)受限于它自身運行的的平臺終端,這一點注定使移動端不像PC前端那樣能夠處理大量的復雜的業(yè)務場景伪冰。然而隨著移動平臺的各種深入,我們不的不考慮這個問題誓酒。傳統(tǒng)的Model數(shù)據(jù)大多來源于網(wǎng)絡數(shù)據(jù),拿到網(wǎng)絡數(shù)據(jù)后客戶端要做的事情就是將數(shù)據(jù)直接按照順序畫在界面上。隨著業(yè)務的越來越來的深入,我們依賴的service服務可能在大多時間無法第一時間滿足客戶端需要的數(shù)據(jù)需求,移動端愈發(fā)的要自行處理一部分邏輯計算操作贮聂。這個時間一慣的做法是在控制器中處理,最終導致了控制器成了垃圾箱,越來越不可維護靠柑。
控制器Controller是app的“膠水代碼”:協(xié)調(diào)模型和視圖之間的所有交互∠判福控制器負責管理他們所擁有的視圖的視圖層次結(jié)構(gòu),還要響應視圖的loading歼冰、appearing、disappearing等等,同時往往也會充滿我們不愿暴露的Model的模型邏輯以及不愿暴露給視圖的業(yè)務邏輯耻警。這引出了第一個關(guān)于MVC的問題...
視圖view通常是UIKit控件(component,這里根據(jù)習慣譯為控件)或者編碼定義的UIKit控件的集合隔嫡。進入.xib或者Storyboard會發(fā)現(xiàn)一個app、Button甘穿、Label都是由這些可視化的和可交互的控件組成腮恩。你懂的。View不應該直接引用Model,并且僅僅通過IBAction事件引用controller温兼。業(yè)務邏輯很明顯不歸入view,視圖本身沒有任何業(yè)務秸滴。
厚重的View Controller由于大量的代碼被放進viewcontroller,導致他們變的相當臃腫。在iOS中有的view controller里綿延成千上萬行代碼的事并不是前所未見的募判。這些超重app的突出情況包括:厚重的View Controller很難維護(由于其龐大的規(guī)模)荡含;包含幾十個屬性,使他們的狀態(tài)難以管理吝羞;遵循許多協(xié)議(protocol),導致協(xié)議的響應代碼和controller的邏輯代碼混淆在一起。
厚重的view controller很難測試,不管是手動測試或是使用單元測試,因為有太多可能的狀態(tài)内颗。將代碼分解成更小的多個模塊通常是件好事。
太過于輕量級的Model
太過于輕量級的Model,早期的Model層,其實就是如果數(shù)據(jù)有幾個屬性,就定義幾個屬性,ARC普及以后我們在Model層的實現(xiàn)文件中基本上看不到代碼( 無需再手動管理釋放變量,Model既沒有復雜的業(yè)務處理,也沒有對象的構(gòu)造,基本上.m文件中的代碼普遍是空的)敦腔;同時與控制器的代碼越來厚重形成強烈的反差,這一度讓人不禁對現(xiàn)有的開發(fā)設計構(gòu)思有所懷疑均澳。
遺失的網(wǎng)絡邏輯
蘋果使用的MVC的定義是這么說的:所有的對象都可以被歸類為一個Model,一個view,或是一個控制器。就這些符衔。那么把網(wǎng)絡代碼放哪里找前?和一個API通信的代碼應該放在哪兒?
你可能試著把它放在Model對象里,但是也會很棘手,因為網(wǎng)絡調(diào)用應該使用異步,這樣如果一個網(wǎng)絡請求比持有它的Model生命周期更長,事情將變的復雜判族。顯然也不應該把網(wǎng)絡代碼放在view里,因此只剩下控制器了躺盛。這同樣是個壞主意,因為這加劇了厚重控制器的問題。
那么應該放在那里呢形帮?顯然MVC的3大組件根本沒有適合放這些代碼的地方槽惫。
較差的可測試性
MVC的另一個大問題是,它不鼓勵開發(fā)人員編寫單元測試。由于控制器混合了視圖處理邏輯和業(yè)務邏輯,分離這些成分的單元測試成了一個艱巨的任務辩撑。大多數(shù)人選擇忽略這個任務,那就是不做任何測試界斜。
上文提到了控制器可以管理視圖的層次結(jié)構(gòu);控制器有一個“view”屬性,并且可以通過IBOutlet訪問視圖的任何子視圖合冀。當有很多outlet時這樣做不易于擴展,在某種意義上,最好不要使用子視圖控制器(child view controller)來幫助管理子視圖各薇。
在這里有多個模糊的標準,似乎沒有人能完全達成一致。貌似無論如何,view和對應的controller都緊緊的耦合在一起,總之,還是會把它們當成一個組件來對待君躺。Apple提供的這個組件一度以來在某種程度誤導了大多初學者,初學者將所有的視圖全部拖到xib中,連接大量的IBoutLet輸出口屬性,都是一些列問題峭判。
MVVM
我們采用MVC架構(gòu)模式的避免了model個view層的通訊,但是事實上也不是絕對的 model層和view層有時候可以跨越controller進行直接通訊, 這一部分的數(shù)據(jù)和視圖之間建立了相互的關(guān)聯(lián),對MVC架構(gòu)模式的主旨思想是違背的.于是,有的人就開始想辦法經(jīng)這部分相互關(guān)聯(lián)的View-Controller抽離出來,作為新的架構(gòu)組件來處理,響應的MVVM就出現(xiàn)在我們的視野中了 .....MVVM據(jù)說最早在微軟的.NET平臺中出現(xiàn)過。在MVVM中他的設計思路和MVC很像棕叫。它正式規(guī)范了視圖和控制器緊耦合的性質(zhì),并引入新的組件林螃。
在MVVM里,view和view controller正式聯(lián)系在一起,我們把它們視為一個組件。視圖view仍然不能直接引用模型Model,當然controller也不能俺泣。相反,他們引用視圖模型view Model治宣。view Model是一個放置用戶輸入驗證邏輯,視圖顯示邏輯,發(fā)起網(wǎng)絡請求和其他各種各樣的代碼的極好的地方。有一件事情不應歸入view Model,那就是任何視圖本身的引用砌滞。view Model的概念同時適用于于iOS和OS X
由于展示邏輯(presentation logic)放在了view Model中(比如Model的值映射到一個格式化的字符串),視圖控制器本身就會不再臃腫侮邀。當你開始使用MVVM的最好方式是,可以先將一小部分邏輯放入視圖模型,然后當你逐漸習慣于使用這個范式的時候再遷移更多的邏輯到視圖模型中.使用MVVM會輕微的增加代碼量,但總體上減少了代碼的復雜性. 再來看MVVM的圖示,你會注意到我使用了模糊的動詞“notify”和“update”,而沒有詳細說明該怎么做。你可以使用KVO,就像MVC那樣,但這很快就會變得難以管理
MVP
上述MVC贝润、MVVM,真實的業(yè)務場景中,如果場景的邏輯異常復雜,在反復的迭代中仍會出現(xiàn)各式各樣的問題绊茧。真對MVVM我個人理解主要是將原來Controller中處理數(shù)據(jù)邏輯的代碼統(tǒng)一歸到一個新的class(viewModel)中去,更甚之網(wǎng)絡請求等工作全部從Controller移到viewModel。剛一開始總覺的怪怪的〈蚓颍現(xiàn)階段客戶端開發(fā)越來越進入一個2.0的階段,早期的app功能都相對比較簡單,無論是從界面還是從業(yè)務邏輯上給人的感覺都是簡潔實用,這中間包括UI的設計华畏、功能的設計鹏秋、產(chǎn)品的設計定位等。隨著行業(yè)的深入,用戶的過渡依賴移動端最終導致業(yè)各式各樣的業(yè)務更加依賴客戶端,這就導致客戶端的開發(fā)不得不向PC端靠齊,在版本的反復迭代中業(yè)務場景變的愈發(fā)不盡人意,仿佛又回到了軟件設計的早期亡笑。
在傳統(tǒng)軟件領域,從MVC的誕生主要是為了解決軟件界面的行為的分離,在復雜的業(yè)務場景內(nèi)會進一步區(qū)分業(yè)務邏輯場景的分離,這些手段的最終的目的都是盡最大限度的降低整個場景的藕合度,使其達到分離的目的,模塊與模塊最終得到獨立,將整個場景化整為零,最終使每個模塊在一個零上工作,這對于無論是軟件的開發(fā)還是后續(xù)的維護侣夷、以及使用普遍遵循這個原則,現(xiàn)有的模式大概產(chǎn)生了相關(guān)的類似架構(gòu)。
web段以及其他業(yè)務層負責從接口層獲取數(shù)據(jù)并執(zhí)行自己的邏輯
service層為外部提供接口
DTO從負責從DB鏈接并進行數(shù)據(jù)讀寫操作
DB層(物理機負責數(shù)據(jù)存儲)
現(xiàn)有客戶度一度采用下面的模式:
客戶端通過service拿到json 數(shù)據(jù),然后通過MVC的結(jié)構(gòu)展示到UI界面上,在iOS中一直流行MVC的開發(fā)模式,通過與傳統(tǒng)開發(fā)模式對比可以發(fā)現(xiàn),其實
service層-客戶端交互與服務端service服務滿足外部業(yè)務場景無非是兩個互逆的過程(一個輸出層,一個輸入層,都是為了更好的滿足的下一步的業(yè)務需求,一個是將原始數(shù)據(jù)邏輯話,一個是將獲得邏輯數(shù)據(jù)存檔并且展示到用戶面前)仑乌。service層根據(jù)具體的業(yè)務場景提供對應的數(shù)據(jù)服務,service根據(jù)不同的業(yè)務場景通過DTO層拿到對應
的數(shù)據(jù)然后組織好數(shù)據(jù)提供給外界(service 層負責將原始物理數(shù)據(jù)轉(zhuǎn)換成對應的邏輯數(shù)據(jù)提供給外界)百拓。
相反,客戶端通過網(wǎng)絡層拿到對應的網(wǎng)絡數(shù)據(jù)繪制到對應的View上,但是實際的開發(fā)過程中,網(wǎng)絡數(shù)據(jù)與真實客戶端使用場景也是有一定的差距,MVVM層將對應的
一部分邏輯處理移植到了ViewModel中,這并沒有從根本上解決問題,無非是將代碼做了一份對應的copy轉(zhuǎn)移,并沒有從根本上達到邏輯分層的概念。相反MVP模
式恰好解決了這一難題,MVP模式衍生于傳統(tǒng)service架構(gòu),針對不同的業(yè)務場景圖供對應的匹配的抽象service服務,客戶端拿到網(wǎng)絡數(shù)據(jù)后未達到指定的目的,
為滿足相同抽象邏輯的業(yè)務場景,在客戶端網(wǎng)絡層與Model層之間加一協(xié)議層,Model層實現(xiàn)整個協(xié)議層,之后在基于MVC的結(jié)構(gòu)下將一概相同層次的
業(yè)務場景繪制解釋到對應的View上晰甚。
M : 邏輯Model層
V : 視圖層
P : protocol協(xié)議層
Model層類似于MVVM的ViewModel,主要負責存儲抽象邏輯數(shù)據(jù),另外Model層主還有部分工作實現(xiàn)對應的協(xié)議層協(xié)議,提供協(xié)議對應的各種屬性以及服務衙传。Model經(jīng)過協(xié)議層抽象約束,最后Model被抽象成具有統(tǒng)一抽象邏輯的業(yè)務場景,最終Model層在講數(shù)據(jù)交付整個MVC結(jié)構(gòu)繪制展示的時間,我們可以按照同一套抽象的邏輯標準去執(zhí)行。
在傳統(tǒng)的web層面,為了滿足各式各樣的業(yè)務邏輯場景服務,最紅我們實現(xiàn)軟件羅杰的層次的分離,誕生了service服務這個概念(service就類似一個標準尺寸的水龍頭出口,只要對應的水龍頭都按照這樣的規(guī)則來生產(chǎn),service就能夠滿足格式各樣的業(yè)務場景,極大的解決的傳統(tǒng)軟件服務業(yè)務場景層次的一系列難題)厕九;相同的原理在客戶端同樣可以使用,為了滿足客戶端MVC結(jié)構(gòu)層里面的穩(wěn)定,避免各式各樣的業(yè)務場景迭代插入不同的邏輯,避免最終軟件危機的產(chǎn)生,我們采用追加協(xié)議層的模式來滿足這一目的蓖捶。
遍觀整個軟件開發(fā),從早期的軟件開發(fā),到后來軟件生產(chǎn)管理的危機,軟件開發(fā)模式一步步的確立,軟件行業(yè)的每個階段都是一個里程碑。這世間沒有相對完美獨到的設計法則,但是亙古不變永遠只有一個那就是軟件的開發(fā)更佳面相生產(chǎn)化扁远、規(guī)范化俊鱼、更加的利于可維護化。一直以來我本人并不特別的注重軟件的設計一定畅买、必須按照某種規(guī)則來做,畢竟不同的人亭引、不同的業(yè)務場景、不同的工程師總有不同的實際境況,站在一個開發(fā)工程師的角度來說我并不固執(zhí)于都按照固定的規(guī)則來(比如說你必須按照某個模式來做,必須用MVVM來做皮获;必須用ReactCocoa信號型機制來做...)焙蚓。相反我個人認為太過于固執(zhí)只不過某些人的一廂情愿的罷了。相反我覺得因地制宜洒宝、應運而生豈不更加快哉,設計不拘于模式,更多時間更是不局限于思考购公。無論是MVVM、MVP哪一個不是脫胎于MVC,這個世間萬變不離其宗,萬千功法始終都離不開一部最終的母經(jīng)雁歌。