MVC
- 經(jīng)典就是經(jīng)典,沒(méi)有之一。iOS中MVC架構(gòu),看懂斯坦福大學(xué)白胡子老頭這張圖基本上就可以了。

- 簡(jiǎn)單理解,就是Controller對(duì)象擁有View和Model對(duì)象,兩者通過(guò)Controller進(jìn)行溝通。對(duì)于單個(gè)頁(yè)面,三個(gè)類就搞定了,感覺(jué)很簡(jiǎn)單。

網(wǎng)絡(luò)連接應(yīng)該放在哪里?Model中嗎?感覺(jué)很有道理?實(shí)際上,很多的網(wǎng)絡(luò)連接的發(fā)起和接收后的處理都放在了Controller中,因?yàn)榉奖懵?。Model一般只有屬性定義,沒(méi)有實(shí)現(xiàn)。
View應(yīng)該是獨(dú)立一塊了吧?實(shí)際上呢,View大多都放在了Controller中,有個(gè)loadView函數(shù),很方便啊。有幾個(gè)人會(huì)單獨(dú)寫(xiě)個(gè)類來(lái)作為view?
本來(lái),xib和Storyboard是很好的分離view的方式。但是,由于“不合適多人合作,版本管理”,非要代碼寫(xiě)界面,還振振有詞:“性能高,對(duì)培養(yǎng)新人有好處”。“謊言說(shuō)100次都能成真話”,何況這些理由聽(tīng)上去還那么有理。
像“檢查用戶名是否合法,檢查密碼對(duì)不對(duì)”應(yīng)該放在哪里呢?有幾個(gè)人會(huì)像斯坦福大學(xué)白胡子老頭那樣新起一個(gè)類來(lái)寫(xiě)?基本上都是Controller中搞定。
BaseController,BaseView,BaseModel一定見(jiàn)過(guò)不少吧?有的還有好幾層呢
公共View,各種名字帶common或者類似的類常見(jiàn)吧?里面網(wǎng)絡(luò)連接,數(shù)據(jù)庫(kù),邏輯等等往往比View本身很多,儼然一個(gè)小模塊了,功能比Controller都強(qiáng)大了。這還是view嗎?
本來(lái)MVC理論上是最簡(jiǎn)單的架構(gòu),但是實(shí)際結(jié)果呢,變成了最難懂的架構(gòu)。Controller成了上帝類,什么都干?!爸恢滥且慧鐤|西有用,但看不出那是簡(jiǎn)單的MVC”。
MVC也被稱之為 Massive View Controller(重量級(jí)視圖控制器)。其實(shí)這不是MVC的錯(cuò),只是沒(méi)有程序員承認(rèn)自己懶惰,編程習(xí)慣不好罷了。如果能夠像斯坦福大學(xué)白胡子老爺爺那樣好的編程習(xí)慣,那么大部分的iOS程序都能有清晰的MVC架構(gòu)。
MVVM
認(rèn)識(shí)MVVM的起點(diǎn)是@objc上文章MVVM 介紹
MVVM來(lái)自MVC,一張經(jīng)典的圖就是下面這張,在好多文章中看到過(guò)。

“稍微考慮一下,雖然 View 和 View Controller 是技術(shù)上不同的組件,但它們幾乎總是手牽手在一起,成對(duì)的。你什么時(shí)候看到一個(gè) View 能夠與不同 View Controller 配對(duì)?或者反過(guò)來(lái)?所以,為什么不正規(guī)化它們的連接呢?” ------ 這段話當(dāng)時(shí)給我的印象很深刻,這個(gè)觀點(diǎn)到現(xiàn)在我都認(rèn)可。
Controller代表了一個(gè)場(chǎng)景(Scene)的生命周期,是一個(gè)調(diào)度者。什么都是,因?yàn)槭裁炊茧x不開(kāi)它。又好像什么都不是,因?yàn)樗聿涣巳魏尉唧w的東西。讓它和View合在一起,作為廣義的view就有了具體的意義,并限制了它無(wú)所不能的印象。這點(diǎn)值得肯定。
“顯示邏輯(presentation logic)”可以從Controller中移到ViewModel中,從而給Controller減負(fù)。這個(gè)觀點(diǎn)我也是支持的。并且我以此認(rèn)為ViewModel就是用來(lái)做“顯示邏輯”的,一個(gè)頁(yè)面一個(gè),隨頁(yè)面而變化。在Swift中,我用結(jié)構(gòu)體來(lái)做ViewModel。
關(guān)于綁定機(jī)制,文章中推薦ReactiveCocoa。我去大致看了一下,主要是將KVO,Block,notification,delegate等各種通訊機(jī)制統(tǒng)一為RACSignal,將界面和數(shù)據(jù)進(jìn)行雙向綁定,功能確實(shí)強(qiáng)大。但是這個(gè)風(fēng)格和普通iOS的開(kāi)發(fā)習(xí)慣差距比較大,一下子很難變過(guò)來(lái)。文章中也說(shuō)只是推薦,不強(qiáng)求,所以我也就一直沒(méi)有采用。被誤解的MVC和被神化的MVVM
至于綁定機(jī)制,在Swift中可以使用屬性觀察者。ViewModel一般作為Controller的一個(gè)屬性,對(duì)它進(jìn)行觀察,一旦變化,就用ViewModel新的值設(shè)置界面元素,感覺(jué)挺好用的。還有一些相隔很遠(yuǎn)或者一對(duì)多的變化,一般可以采用NSNotification來(lái)達(dá)到目的。
從網(wǎng)絡(luò)取數(shù)據(jù),業(yè)務(wù)邏輯(相對(duì)于顯示邏輯),應(yīng)該放在那里呢?文章中沒(méi)有說(shuō),看意思是保留在Controller中。還有的觀點(diǎn)認(rèn)為應(yīng)該放在ViewModel中,這當(dāng)然有道理,而且這是主流的理解。但是這樣會(huì)讓ViewModel變成另外一個(gè)上帝類。
我理解的MVVM
這是本人的理解,僅僅一家之言。主流的觀點(diǎn)沒(méi)有Logic那個(gè)類,從圖中刪除基本上就是了。ViewModel將是替代Controller的一個(gè)上帝類。

Controller主要作為調(diào)度者,居于中心位置??痛糠諺iew相關(guān)功能:比如動(dòng)畫(huà)里面關(guān)于view的位置改變,這些代碼是要放在Controller里面的。這也符合Controller+View實(shí)現(xiàn)view功能的概念。
ViewModel專門(mén)做“顯示邏輯”,并且用屬性觀察者做綁定,必要的時(shí)候用Notification。正向的綁定比如“action-target”響應(yīng)就保留在Controller中,具體事情交個(gè)其他類做就可以了。
在Swift中,ViewModel和Model推薦用Struct;Logic傾向于用class。從一個(gè)簡(jiǎn)單直觀的概念來(lái)說(shuō),ViewModel需要保持輕量級(jí),跟隨頁(yè)面走,隨時(shí)準(zhǔn)備修改。Model也是輕量級(jí),跟隨后臺(tái)API定義走,只是個(gè)數(shù)據(jù)結(jié)構(gòu),隨時(shí)準(zhǔn)備修改。而Logic就顯得比較大,考慮穩(wěn)定,考慮復(fù)用。
增加Logic類,負(fù)責(zé)業(yè)務(wù)邏輯,比如從網(wǎng)絡(luò)取數(shù)據(jù),修改數(shù)據(jù)庫(kù),檢查用戶名合法性,具體的響應(yīng)邏輯,監(jiān)聽(tīng)后的具體處理等等
猿題庫(kù) iOS 客戶端架構(gòu)設(shè)計(jì)
文章中的DataController相當(dāng)于這里的Logic
重點(diǎn)是Controller減負(fù),盡量起調(diào)度者職能,具體工作都放到Logic中處理。
Logic考慮復(fù)用,可以對(duì)應(yīng)單個(gè)頁(yè)面,也可以多個(gè)頁(yè)面共用。按照業(yè)務(wù)邏輯的思路去劃分模塊。劃分標(biāo)準(zhǔn)可以和頁(yè)面分類標(biāo)準(zhǔn)不一樣。
對(duì)于復(fù)雜頁(yè)面,View和ViewModel可以多個(gè),按照組件的模式去考慮。
對(duì)于表格,ViewModel對(duì)應(yīng)的是表格的cell,dataSource數(shù)組中放ViewModel的序列。
表格的delegate和dataSource,目前來(lái)看,放在Controller中是最方便的。當(dāng)然,為了給Controller減負(fù),再新增一個(gè)類TableDelegate也是很不錯(cuò)的方法。
如果表格包含在一個(gè)組件中,用容器view做delegate和dataSource是一個(gè)不錯(cuò)的選擇。
主要想法就是想方設(shè)法“架空”Controller,讓它只做一個(gè)調(diào)度者,管理頁(yè)面的生命周期就可以了。實(shí)在非它不可的時(shí)候,才讓它做具體的事情。
不引入ReactiveCocoa等龐大的第三方庫(kù)。這里有一篇文章不錯(cuò),值得學(xué)習(xí)一下。
ReactiveCocoa 和 MVVM 入門(mén)
VIPER
這是比MVVM分類更細(xì)的一種模式。
經(jīng)典圖形

View: 也是View + Controller
Present:相當(dāng)于ViewModel,叫展示器
Interactor:交互器,側(cè)重于業(yè)務(wù)邏輯;從網(wǎng)絡(luò)取數(shù)據(jù),數(shù)據(jù)庫(kù)等功能都在這里。
Entity:就是Model,僅僅是數(shù)據(jù)定義
WireFrame:就是Router,是頁(yè)面跳轉(zhuǎn)
值得借鑒的地方
將頁(yè)面跳轉(zhuǎn)獨(dú)立出來(lái),做成公共模塊
將業(yè)務(wù)邏輯獨(dú)立出來(lái),做成公共模塊
如果只是對(duì)于單個(gè)頁(yè)面,分這么多類,感覺(jué)有點(diǎn)啰嗦了。不夠?qū)τ诙囗?yè)面的模塊來(lái)說(shuō),還是有借鑒意義的
參考文章
其他架構(gòu)
一些實(shí)際在用,但是沒(méi)有通用縮寫(xiě)名稱的架構(gòu)
分層模式

將服務(wù)service的概念引入客戶端,作為一個(gè)中間層,進(jìn)行隔離
業(yè)務(wù)邏輯作為服務(wù)模塊,通過(guò)服務(wù)的方式進(jìn)行訪問(wèn)
數(shù)據(jù)訪問(wèn)跟業(yè)務(wù)邏輯,表現(xiàn)層分開(kāi),是跟后臺(tái)的接口層
表現(xiàn)層只關(guān)注UI,相當(dāng)于VIPER中的V和P;或者是MVVM中的ViewModel(僅顯示邏輯)和View,但是沒(méi)有雙向綁定;
平臺(tái)模式

當(dāng)前的APP,大多數(shù)是Native和H5的混合,將兩者的接口代碼統(tǒng)一成通用模塊是比較好的做法
插件化也是一個(gè)越來(lái)越普遍的趨勢(shì),比如分享,第三方登錄,支付等等,都由第三方以插件的形式提供。對(duì)這些插件集中管理也是好的做法
APP隨著公司發(fā)展壯大,分出不同的事業(yè)部,在同一公司多個(gè)APP或者不同業(yè)務(wù);這樣就有兩個(gè)相反的發(fā)展趨勢(shì):一方面,想共用模塊,讓多個(gè)業(yè)務(wù)共享;另一方面,各自業(yè)務(wù)又要隔離,獨(dú)立發(fā)展。公司也有可能成立公共的平臺(tái)部。各業(yè)務(wù)部門(mén)之間是縱向拆分;業(yè)務(wù)和平臺(tái)之間是橫向拆分。這就導(dǎo)致二維劃分的立體架構(gòu)。
參考文章
分離出界面層,盡量薄,和UI同學(xué)協(xié)作,快速應(yīng)變
分離數(shù)據(jù)層,盡量薄,與后臺(tái)合作,快速應(yīng)變
一些思考
架構(gòu)設(shè)計(jì)沒(méi)有統(tǒng)一的標(biāo)準(zhǔn),上面接觸到的架構(gòu)模型,都有積極的參考意義,但是都不能照搬。需要根據(jù)自己的實(shí)際情況進(jìn)行一定的權(quán)衡取舍
Step0:平臺(tái)型應(yīng)用
以URL的方式,由主App調(diào)用子App
形式類似于調(diào)用打電話,發(fā)短信,發(fā)郵件
URL的定義需要統(tǒng)籌考慮
Step1:縱向劃分
分Native,H5,插件三部分
Native和H5之間提供統(tǒng)一的橋接模塊
Native和插件之間提供統(tǒng)一的橋架模塊
如果加入ReactNative,那么也要提供Native和ReactNative之間的橋接模塊。這個(gè)可以先預(yù)留,也可以以后再添加。
Step2:橫向劃分
Native部分進(jìn)行橫劃分,因?yàn)檫@一塊是最耗資源的部分
最上層是界面層(名字可以叫表現(xiàn)層或者UI層),這里可以借用MVVM的思想。M不用考慮,由下層以服務(wù)的形式提供。VM僅僅做顯示邏輯,在Swift中用struct。這一層是跟產(chǎn)品的交流層,盡量薄,并且能夠快速應(yīng)對(duì)變化。業(yè)務(wù)邏輯等能分出去的功能,一律分出去。核心和重點(diǎn)就是讓Controller只做調(diào)度者,萬(wàn)不得已可以酌情參與很少一部分的view工作。
最底層是微服務(wù)層(micro service)。這一層提供基本的功能,比如網(wǎng)絡(luò),緩存,加解密,系統(tǒng)信息,日志,統(tǒng)計(jì)等等。微服務(wù)的概念是只能供其他模塊調(diào)用,不能調(diào)用其他模塊的服務(wù)。本層中的模塊之間也不能相互調(diào)用。這里是一些基礎(chǔ)的組件,按照功能劃分,相互間的隔離是第一考慮要素。要求高內(nèi)聚。
中間是服務(wù)層(service),這里的服務(wù)可以調(diào)用微服務(wù),也可以相互之間調(diào)用。
分三層相對(duì)簡(jiǎn)單一點(diǎn)。當(dāng)然也可以分出一些接口層,服務(wù)層還可以分出公共服務(wù)層,業(yè)務(wù)邏輯層等。這個(gè)可以根據(jù)需要靈活配置。但是總體上分三層(界面、服務(wù)、微服務(wù))。
不要跨層調(diào)用,界面層只能調(diào)用服務(wù)層提供的服務(wù)。服務(wù)層可以自己完成工作,也可以調(diào)用其他服務(wù)或者微服務(wù)完成工作。
Step3:層內(nèi)劃分
界面層:按照頁(yè)面進(jìn)行組織,提供公共的UI組件,可以理解為(M)VVM。VM作為將“界面顯示”轉(zhuǎn)換為“數(shù)據(jù)操作”的媒介,利用Swift的屬性觀察者特性,進(jìn)行一級(jí)綁定。不引入RxSwift等函數(shù)式編程的大型第三方庫(kù)。
服務(wù)層:分為公共服務(wù),跳轉(zhuǎn)邏輯,業(yè)務(wù)邏輯等模塊,按照邏輯功能劃分。跟具體頁(yè)面不必相關(guān),跟界面層的接口為各ViewModel種定義的協(xié)議。
微服務(wù)層:按照功能劃分,不設(shè)計(jì)業(yè)務(wù)邏輯,分網(wǎng)絡(luò)、數(shù)據(jù)庫(kù)、加解密,日志,統(tǒng)計(jì)等功能
框架圖

語(yǔ)言選擇Swift,最低支持版本iOS8,有條件的從iOS9開(kāi)始
服務(wù)和微服務(wù)都以framework的形式提供,模塊間的隔離需要重點(diǎn)考慮。
服務(wù)service和微服務(wù)僅僅是邏輯上的層次結(jié)構(gòu),在具體的工程組織上,都采用一級(jí)framework封裝,相互間的層級(jí)和調(diào)用采用相互間的依賴隱含表示。
提供一個(gè)界面隔離的service.framework,界面層只調(diào)用它完成所有任務(wù)。作用相當(dāng)于Foundation。
以workspace的方式組織工程,第三方管理工具采用Carthage。有條件的情況下,微服務(wù)以及部分服務(wù)可以采用私有Carthage的形式,更方便復(fù)用。
插件也要求以framework的形式提供,不接受.a的靜態(tài)庫(kù)。
如果暫時(shí)需要用到Object-C、C、C++,都統(tǒng)一成framework的形式,以后逐步用Swift替換。
類圖

界面層
AppDelegate、ViewController僅僅作為調(diào)度者存在,不做任何具體的事情。
ViewModel僅僅做顯示邏輯相關(guān)的事情,僅僅起到將界面轉(zhuǎn)換為數(shù)據(jù)的作用。用結(jié)構(gòu)體struct,每個(gè)成員都是普通變量,并且都有默認(rèn)值,代表了頁(yè)面的確定性。ViewModel是一種數(shù)據(jù)結(jié)構(gòu),做顯示邏輯的事情。
UI組件僅由View和ViewModel組成,只能包含顯示邏輯,不能包含跳轉(zhuǎn)邏輯,業(yè)務(wù)邏輯等。
ViewModel放UI層和Service層都有一定道理:放UI層表示顯示邏輯;放service層表示UI和service之間的數(shù)據(jù)接口??紤]再三,覺(jué)得還是應(yīng)該放UI層。ViewModel最大的作用還是在于將UI變化轉(zhuǎn)變?yōu)閿?shù)據(jù)操作,本質(zhì)上離UI應(yīng)該更近一些。
至于UI層和Service層之間的接口,還是定義相關(guān)的的ViewModel protocol比較好。這些protocol的定義放在service中(由于framework的影響),將ViewModel的一些基本需求放在protocol中。service中用Model或者還是用其他來(lái)滿足這個(gè)protocol,就不做要求了。用protocol作為接口比單純用Model做接口要好。因?yàn)镸odel隨著后臺(tái)API定義而變,而protocol只相當(dāng)于一個(gè)基類,類型更靈活。

除了定義一個(gè)ViewModel的protocol之外,再定義一個(gè)是service的protocol,(既然引入service概念,就可以淡化logic和data的概念)。
界面層保持最輕量級(jí)。頁(yè)面跳轉(zhuǎn)邏輯,具體業(yè)務(wù)邏輯等工作全部下沉到服務(wù)層來(lái)做。
ViewModel是一個(gè)struct,主要做顯示邏輯,概念相對(duì)比較小,一般一個(gè)頁(yè)面一個(gè)或者多個(gè)。而service是一個(gè)類,概念比較大,可以多個(gè)頁(yè)面共用一個(gè)。尺度可以根據(jù)具體情況靈活掌握。不同的頁(yè)面,通過(guò)擴(kuò)展遵循不同的協(xié)議區(qū)分開(kāi)來(lái)。類本身可能比較大,但是每一部分都是相對(duì)比較小的。

服務(wù)層
service.framework作為一個(gè)粘合層存在,AppDelegate、ViewController只要import service就可以調(diào)用相關(guān)服務(wù)了。
金融.framework、保險(xiǎn).framework等屬于業(yè)務(wù)特有的邏輯
Router、用戶、分享等屬于業(yè)務(wù)無(wú)關(guān)的公共邏輯
層內(nèi)的各模塊間可以相互調(diào)用
具體存在形式,單例、類、或者framework等,可根據(jù)具體情況靈活決定。
為了結(jié)構(gòu)清晰,圖中的調(diào)用關(guān)系線只畫(huà)出了很少的一部分,大部分線都沒(méi)有畫(huà)出來(lái)。
微服務(wù)層
從開(kāi)發(fā)的角度,按照功能分類;是工程師之間交流的技術(shù)語(yǔ)言,而不是跟產(chǎn)品交流的業(yè)務(wù)語(yǔ)言
功能高內(nèi)聚,作為被調(diào)用的基礎(chǔ)模塊
模塊之間不要存在相互調(diào)用的關(guān)系
以framework的形式存在。高內(nèi)聚,高復(fù)用。