MVC
經(jīng)典就是經(jīng)典桃序,沒有之一战惊。iOS中MVC架構(gòu)信柿,看懂斯坦福大學(xué)白胡子老頭這張圖基本上就可以了。
斯坦福大學(xué)MVC架構(gòu).png
簡單理解舆声,就是Controller對(duì)象擁有View和Model對(duì)象花沉,兩者通過Controller進(jìn)行溝通。對(duì)于單個(gè)頁面媳握,三個(gè)類就搞定了碱屁,感覺很簡單。
MVC.jpg
網(wǎng)絡(luò)連接應(yīng)該放在哪里蛾找?Model中嗎娩脾?感覺很有道理?實(shí)際上打毛,很多的網(wǎng)絡(luò)連接的發(fā)起和接收后的處理都放在了Controller中晦雨,因?yàn)榉奖懵锛懿堋odel一般只有屬性定義,沒有實(shí)現(xiàn)闹瞧。
View應(yīng)該是獨(dú)立一塊了吧?實(shí)際上呢展辞,View大多都放在了Controller中奥邮,有個(gè)loadView函數(shù),很方便啊罗珍。有幾個(gè)人會(huì)單獨(dú)寫個(gè)類來作為view洽腺?
本來,xib和Storyboard是很好的分離view的方式覆旱。但是蘸朋,由于“不合適多人合作,版本管理”扣唱,非要代碼寫界面藕坯,還振振有詞:“性能高,對(duì)培養(yǎng)新人有好處”噪沙×侗耄“謊言說100次都能成真話”,何況這些理由聽上去還那么有理正歼。
像“檢查用戶名是否合法辐马,檢查密碼對(duì)不對(duì)”應(yīng)該放在哪里呢?有幾個(gè)人會(huì)像斯坦福大學(xué)白胡子老頭那樣新起一個(gè)類來寫局义?基本上都是Controller中搞定喜爷。
BaseController,BaseView萄唇,BaseModel一定見過不少吧檩帐?有的還有好幾層呢
公共View,各種名字帶common或者類似的類常見吧穷绵?里面網(wǎng)絡(luò)連接轿塔,數(shù)據(jù)庫,邏輯等等往往比View本身很多仲墨,儼然一個(gè)小模塊了勾缭,功能比Controller都強(qiáng)大了。這還是view嗎目养?
本來MVC理論上是最簡單的架構(gòu)俩由,但是實(shí)際結(jié)果呢,變成了最難懂的架構(gòu)癌蚁。Controller成了上帝類幻梯,什么都干兜畸。“只知道那一坨東西有用碘梢,但看不出那是簡單的MVC”咬摇。
MVC也被稱之為 Massive View Controller(重量級(jí)視圖控制器)。其實(shí)這不是MVC的錯(cuò)煞躬,只是沒有程序員承認(rèn)自己懶惰肛鹏,編程習(xí)慣不好罷了。如果能夠像斯坦福大學(xué)白胡子老爺爺那樣好的編程習(xí)慣恩沛,那么大部分的iOS程序都能有清晰的MVC架構(gòu)在扰。
MVVM
認(rèn)識(shí)MVVM的起點(diǎn)是@objc上文章MVVM 介紹
MVVM來自MVC,一張經(jīng)典的圖就是下面這張雷客,在好多文章中看到過芒珠。
MVVM.gif
“稍微考慮一下,雖然 View 和 View Controller 是技術(shù)上不同的組件搅裙,但它們幾乎總是手牽手在一起皱卓,成對(duì)的。你什么時(shí)候看到一個(gè) View 能夠與不同 View Controller 配對(duì)呈宇?或者反過來好爬?所以,為什么不正規(guī)化它們的連接呢甥啄?” ------ 這段話當(dāng)時(shí)給我的印象很深刻存炮,這個(gè)觀點(diǎn)到現(xiàn)在我都認(rèn)可。
Controller代表了一個(gè)場景(Scene)的生命周期蜈漓,是一個(gè)調(diào)度者穆桂。什么都是,因?yàn)槭裁炊茧x不開它融虽。又好像什么都不是享完,因?yàn)樗聿涣巳魏尉唧w的東西。讓它和View合在一起有额,作為廣義的view就有了具體的意義般又,并限制了它無所不能的印象。這點(diǎn)值得肯定巍佑。
“顯示邏輯(presentation logic)”可以從Controller中移到ViewModel中茴迁,從而給Controller減負(fù)。這個(gè)觀點(diǎn)我也是支持的萤衰。并且我以此認(rèn)為ViewModel就是用來做“顯示邏輯”的堕义,一個(gè)頁面一個(gè),隨頁面而變化脆栋。在Swift中倦卖,我用結(jié)構(gòu)體來做ViewModel洒擦。
關(guān)于綁定機(jī)制,文章中推薦ReactiveCocoa怕膛。我去大致看了一下熟嫩,主要是將KVO,Block嘉竟,notification邦危,delegate等各種通訊機(jī)制統(tǒng)一為RACSignal,將界面和數(shù)據(jù)進(jìn)行雙向綁定舍扰,功能確實(shí)強(qiáng)大。但是這個(gè)風(fēng)格和普通iOS的開發(fā)習(xí)慣差距比較大希坚,一下子很難變過來边苹。文章中也說只是推薦,不強(qiáng)求裁僧,所以我也就一直沒有采用个束。被誤解的MVC和被神化的MVVM
至于綁定機(jī)制,在Swift中可以使用屬性觀察者聊疲。ViewModel一般作為Controller的一個(gè)屬性茬底,對(duì)它進(jìn)行觀察,一旦變化获洲,就用ViewModel新的值設(shè)置界面元素阱表,感覺挺好用的。還有一些相隔很遠(yuǎn)或者一對(duì)多的變化贡珊,一般可以采用NSNotification來達(dá)到目的最爬。
從網(wǎng)絡(luò)取數(shù)據(jù),業(yè)務(wù)邏輯(相對(duì)于顯示邏輯)门岔,應(yīng)該放在那里呢爱致?文章中沒有說,看意思是保留在Controller中寒随。還有的觀點(diǎn)認(rèn)為應(yīng)該放在ViewModel中糠悯,這當(dāng)然有道理,而且這是主流的理解妻往。但是這樣會(huì)讓ViewModel變成另外一個(gè)上帝類互艾。
我理解的MVVM
這是本人的理解,僅僅一家之言蒲讯。主流的觀點(diǎn)沒有Logic那個(gè)類忘朝,從圖中刪除基本上就是了。ViewModel將是替代Controller的一個(gè)上帝類判帮。
MVVM.jpg
Controller主要作為調(diào)度者局嘁,居于中心位置溉箕。客串部分View相關(guān)功能:比如動(dòng)畫里面關(guān)于view的位置改變悦昵,這些代碼是要放在Controller里面的肴茄。這也符合Controller+View實(shí)現(xiàn)view功能的概念。
ViewModel專門做“顯示邏輯”但指,并且用屬性觀察者做綁定寡痰,必要的時(shí)候用Notification。正向的綁定比如“action-target”響應(yīng)就保留在Controller中棋凳,具體事情交個(gè)其他類做就可以了拦坠。
在Swift中,ViewModel和Model推薦用Struct剩岳;Logic傾向于用class贞滨。從一個(gè)簡單直觀的概念來說,ViewModel需要保持輕量級(jí)拍棕,跟隨頁面走晓铆,隨時(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ù)庫奏属,檢查用戶名合法性,具體的響應(yīng)邏輯潮峦,監(jiān)聽后的具體處理等等
文章中的DataController相當(dāng)于這里的Logic
重點(diǎn)是Controller減負(fù)囱皿,盡量起調(diào)度者職能,具體工作都放到Logic中處理忱嘹。
Logic考慮復(fù)用嘱腥,可以對(duì)應(yīng)單個(gè)頁面,也可以多個(gè)頁面共用拘悦。按照業(yè)務(wù)邏輯的思路去劃分模塊齿兔。劃分標(biāo)準(zhǔn)可以和頁面分類標(biāo)準(zhǔn)不一樣。
對(duì)于復(fù)雜頁面,View和ViewModel可以多個(gè)分苇,按照組件的模式去考慮添诉。
對(duì)于表格,ViewModel對(duì)應(yīng)的是表格的cell医寿,dataSource數(shù)組中放ViewModel的序列栏赴。
表格的delegate和dataSource,目前來看靖秩,放在Controller中是最方便的须眷。當(dāng)然,為了給Controller減負(fù)沟突,再新增一個(gè)類TableDelegate也是很不錯(cuò)的方法花颗。
如果表格包含在一個(gè)組件中,用容器view做delegate和dataSource是一個(gè)不錯(cuò)的選擇惠拭。
主要想法就是想方設(shè)法“架空”Controller捎稚,讓它只做一個(gè)調(diào)度者,管理頁面的生命周期就可以了求橄。實(shí)在非它不可的時(shí)候,才讓它做具體的事情葡公。
不引入ReactiveCocoa等龐大的第三方庫罐农。這里有一篇文章不錯(cuò),值得學(xué)習(xí)一下催什。
VIPER
這是比MVVM分類更細(xì)的一種模式涵亏。
經(jīng)典圖形
VIPER.png
View: 也是View + Controller
Present:相當(dāng)于ViewModel,叫展示器
Interactor:交互器蒲凶,側(cè)重于業(yè)務(wù)邏輯气筋;從網(wǎng)絡(luò)取數(shù)據(jù),數(shù)據(jù)庫等功能都在這里旋圆。
Entity:就是Model宠默,僅僅是數(shù)據(jù)定義
WireFrame:就是Router,是頁面跳轉(zhuǎn)
值得借鑒的地方
將頁面跳轉(zhuǎn)獨(dú)立出來灵巧,做成公共模塊
將業(yè)務(wù)邏輯獨(dú)立出來搀矫,做成公共模塊
如果只是對(duì)于單個(gè)頁面,分這么多類刻肄,感覺有點(diǎn)啰嗦了瓤球。不夠?qū)τ诙囗撁娴哪K來說,還是有借鑒意義的
參考文章
其他架構(gòu)
一些實(shí)際在用敏弃,但是沒有通用縮寫名稱的架構(gòu)
分層模式
分層架構(gòu).png
將服務(wù)service的概念引入客戶端卦羡,作為一個(gè)中間層,進(jìn)行隔離
業(yè)務(wù)邏輯作為服務(wù)模塊,通過服務(wù)的方式進(jìn)行訪問
數(shù)據(jù)訪問跟業(yè)務(wù)邏輯绿饵,表現(xiàn)層分開欠肾,是跟后臺(tái)的接口層
表現(xiàn)層只關(guān)注UI,相當(dāng)于VIPER中的V和P蝴罪;或者是MVVM中的ViewModel(僅顯示邏輯)和View董济,但是沒有雙向綁定;
平臺(tái)模式
平臺(tái)架構(gòu).png
當(dāng)前的APP要门,大多數(shù)是Native和H5的混合虏肾,將兩者的接口代碼統(tǒng)一成通用模塊是比較好的做法
插件化也是一個(gè)越來越普遍的趨勢,比如分享欢搜,第三方登錄封豪,支付等等,都由第三方以插件的形式提供炒瘟。對(duì)這些插件集中管理也是好的做法
APP隨著公司發(fā)展壯大吹埠,分出不同的事業(yè)部,在同一公司多個(gè)APP或者不同業(yè)務(wù)疮装;這樣就有兩個(gè)相反的發(fā)展趨勢:一方面缘琅,想共用模塊,讓多個(gè)業(yè)務(wù)共享廓推;另一方面刷袍,各自業(yè)務(wù)又要隔離,獨(dú)立發(fā)展樊展。公司也有可能成立公共的平臺(tái)部呻纹。各業(yè)務(wù)部門之間是縱向拆分;業(yè)務(wù)和平臺(tái)之間是橫向拆分专缠。這就導(dǎo)致二維劃分的立體架構(gòu)雷酪。
參考文章
分離出界面層,盡量薄涝婉,和UI同學(xué)協(xié)作哥力,快速應(yīng)變
分離數(shù)據(jù)層,盡量薄嘁圈,與后臺(tái)合作省骂,快速應(yīng)變
一些思考
架構(gòu)設(shè)計(jì)沒有統(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)度者,萬不得已可以酌情參與很少一部分的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ì)簡單一點(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)劃分
界面層:按照頁面進(jìn)行組織切厘,提供公共的UI組件,可以理解為(M)VVM懊缺。VM作為將“界面顯示”轉(zhuǎn)換為“數(shù)據(jù)操作”的媒介疫稿,利用Swift的屬性觀察者特性,進(jìn)行一級(jí)綁定鹃两。不引入RxSwift等函數(shù)式編程的大型第三方庫遗座。
服務(wù)層:分為公共服務(wù),跳轉(zhuǎn)邏輯俊扳,業(yè)務(wù)邏輯等模塊途蒋,按照邏輯功能劃分。跟具體頁面不必相關(guān)馋记,跟界面層的接口為各ViewModel種定義的協(xié)議号坡。
微服務(wù)層:按照功能劃分,不設(shè)計(jì)業(yè)務(wù)邏輯梯醒,分網(wǎng)絡(luò)宽堆、數(shù)據(jù)庫、加解密茸习,日志畜隶,統(tǒng)計(jì)等功能
框架圖
模塊.jpg
語言選擇Swift,最低支持版本iOS8号胚,有條件的從iOS9開始
服務(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)庫击碗。
如果暫時(shí)需要用到Object-C筑悴、C、C++稍途,都統(tǒng)一成framework的形式阁吝,以后逐步用Swift替換。
類圖
架構(gòu)類圖.jpg
界面層
AppDelegate械拍、ViewController僅僅作為調(diào)度者存在突勇,不做任何具體的事情。
ViewModel僅僅做顯示邏輯相關(guān)的事情坷虑,僅僅起到將界面轉(zhuǎn)換為數(shù)據(jù)的作用甲馋。用結(jié)構(gòu)體struct,每個(gè)成員都是普通變量迄损,并且都有默認(rèn)值定躏,代表了頁面的確定性。ViewModel是一種數(shù)據(jù)結(jié)構(gòu)芹敌,做顯示邏輯的事情痊远。
UI組件僅由View和ViewModel組成,只能包含顯示邏輯氏捞,不能包含跳轉(zhuǎn)邏輯拗引,業(yè)務(wù)邏輯等。
ViewModel放UI層和Service層都有一定道理:放UI層表示顯示邏輯幌衣;放service層表示UI和service之間的數(shù)據(jù)接口∪烂担考慮再三豁护,覺得還是應(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或者還是用其他來滿足這個(gè)protocol,就不做要求了达址。用protocol作為接口比單純用Model做接口要好蔑祟。因?yàn)镸odel隨著后臺(tái)API定義而變,而protocol只相當(dāng)于一個(gè)基類沉唠,類型更靈活疆虚。
ViewModel.jpg
除了定義一個(gè)ViewModel的protocol之外,再定義一個(gè)是service的protocol满葛,(既然引入service概念径簿,就可以淡化logic和data的概念)。
界面層保持最輕量級(jí)嘀韧。頁面跳轉(zhuǎn)邏輯篇亭,具體業(yè)務(wù)邏輯等工作全部下沉到服務(wù)層來做。
ViewModel是一個(gè)struct译蒂,主要做顯示邏輯蹂随,概念相對(duì)比較小蹦魔,一般一個(gè)頁面一個(gè)或者多個(gè)乒躺。而service是一個(gè)類,概念比較大,可以多個(gè)頁面共用一個(gè)银觅。尺度可以根據(jù)具體情況靈活掌握。不同的頁面,通過擴(kuò)展遵循不同的協(xié)議區(qū)分開來万皿。類本身可能比較大芝雪,但是每一部分都是相對(duì)比較小的。
Protocol.jpg
服務(wù)層
service.framework作為一個(gè)粘合層存在晤柄,AppDelegate惠勒、ViewController只要import service就可以調(diào)用相關(guān)服務(wù)了盾计。
金融.framework、保險(xiǎn).framework等屬于業(yè)務(wù)特有的邏輯
Router炫乓、用戶创橄、分享等屬于業(yè)務(wù)無關(guān)的公共邏輯
層內(nèi)的各模塊間可以相互調(diào)用
具體存在形式安吁,單例、類、或者framework等航徙,可根據(jù)具體情況靈活決定。
為了結(jié)構(gòu)清晰讹躯,圖中的調(diào)用關(guān)系線只畫出了很少的一部分骗灶,大部分線都沒有畫出來。
微服務(wù)層
從開發(fā)的角度秉馏,按照功能分類耙旦;是工程師之間交流的技術(shù)語言,而不是跟產(chǎn)品交流的業(yè)務(wù)語言
功能高內(nèi)聚萝究,作為被調(diào)用的基礎(chǔ)模塊
模塊之間不要存在相互調(diào)用的關(guān)系
以framework的形式存在免都。高內(nèi)聚锉罐,高復(fù)用。
參考文章
App架構(gòu)經(jīng)驗(yàn)總結(jié)
App架構(gòu)設(shè)計(jì)經(jīng)驗(yàn)談:接口的設(shè)計(jì)
App架構(gòu)設(shè)計(jì)經(jīng)驗(yàn)談:技術(shù)選型
App架構(gòu)設(shè)計(jì)經(jīng)驗(yàn)談:數(shù)據(jù)層的設(shè)計(jì)
App架構(gòu)設(shè)計(jì)經(jīng)驗(yàn)談:業(yè)務(wù)層的設(shè)計(jì)
作者:郭靖888
鏈接:http://www.reibang.com/p/b7ac2f8a6b72
來源:簡書
著作權(quán)歸作者所有绕娘。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán)脓规,非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。