【原文】https://medium.com/@martinmitrevski/pragmatic-ios-app-architecture-f7d6334fd8e4
關(guān)于iOS App架構(gòu)的文章有很多,解決方案有很多伺绽,其中也不乏設(shè)計(jì)精巧的架構(gòu)。但是沒有一種架構(gòu)適合所有的場景帜讲。But,如何選擇癣朗、取舍呢昏翰?下面的評估中有幾條參考規(guī)范。
Evaluating architecture
app中的每個(gè)模塊都應(yīng)該精心的組織并解耦朋魔。模塊之間不需要知道彼此的內(nèi)部詳細(xì)情況。
架構(gòu)要表達(dá)出項(xiàng)目所屬的商業(yè)領(lǐng)域卿操。這是新加入成員能夠快速從代碼中獲取到的信息警检。對于維護(hù)和擴(kuò)展一個(gè)產(chǎn)品孙援,架構(gòu)能進(jìn)行“自我介紹”是至關(guān)重要的。特別是扇雕,在不斷地往里面添加人手的時(shí)候拓售。
可擴(kuò)展性。是否容易往里面添加新的特性镶奉。擁有一個(gè)優(yōu)雅的解決方案可以在未來幫你節(jié)省大量的時(shí)間和金錢础淤。
架構(gòu)是否滿足商業(yè)領(lǐng)域的需求。app是以大量數(shù)據(jù)驅(qū)動的腮鞍?還是有大量需要用戶填寫的表單值骇?app的復(fù)雜程度莹菱,是只有五個(gè)頁面還是有50個(gè)頁面移国?
開發(fā)團(tuán)隊(duì)的工作效率。團(tuán)隊(duì)是否可以快速的理解新的架構(gòu)和不懂得概念道伟?是否可以無障礙的獨(dú)立進(jìn)行開發(fā)工作迹缀?可以想象你的架構(gòu)中只有一個(gè)storyboard,在多人協(xié)作并且都編輯storyboard的情況下蜜徽,合并代碼的時(shí)候可能會掉進(jìn)坑里祝懂!
測試是選擇架構(gòu)的另一個(gè)重要因素。那個(gè)關(guān)鍵模塊是我們想要測試的拘鞋?只對值得測試的部分進(jìn)行測試砚蓬,不要為了代碼覆蓋率(code coverage)而進(jìn)行測試。你可能有90%的代碼覆蓋率但是卻沒有覆蓋到至關(guān)重要的部分盆色。另一個(gè)極端的情況灰蛙,完美的項(xiàng)目設(shè)計(jì),彼此分離的模塊隔躲,但是代碼覆蓋率卻是0摩梧。
現(xiàn)實(shí)情況——另一個(gè)至關(guān)重要的部分。項(xiàng)目的截止日期和預(yù)算情況如何宣旱?在質(zhì)量和交付日期之間如何取舍仅父?工程師需要更多的時(shí)間用最優(yōu)的方式去設(shè)計(jì)和實(shí)現(xiàn)項(xiàng)目,但銷售人員追求的卻是快速浑吟。每個(gè)人都有自己的關(guān)注點(diǎn)笙纤,我們需要有意識的在兩只之間的進(jìn)行平衡。低劣的產(chǎn)品不會持久组力,但是遲來的產(chǎn)品也會被市場所淘汰省容。
因此,實(shí)用忿项、中立(neutrality)蓉冈、大局觀城舞,正是這些方面驅(qū)動著我決定采用什么樣的架構(gòu)。
The good old?MVC (經(jīng)典架構(gòu) - MVC)
相當(dāng)困惑寞酿,為什么那么多人都放棄實(shí)用MVC模式家夺?有人說它代表著復(fù)雜笨重的View Controllers,不適合大型項(xiàng)目伐弹。但是我們有很多概念和模式可以幫助減小View Controllers的大小拉馋。比如:?delegation(代理), composition, dependency injection, protocols(協(xié)議), pure functions(唇方法), service / utility classes, navigation centers等。這些技術(shù)手段使得測試并不是那么的困難惨好。
Going reactive
當(dāng)然煌茴,這還要看app的類型,像facebook這種數(shù)據(jù)驅(qū)動的app就不適合使用MVC日川,fb里面有太多的cells和內(nèi)容蔓腐,使用經(jīng)典的MVC幾乎不太可能。一個(gè)帶有數(shù)據(jù)流的更加被動的方案更適合這種情況(A more reactive approach with uni-directional data flow is more suitable in such cases.)龄句。有一個(gè)很棒的fb架構(gòu)演示可以在這里查看回论。
MVVM
區(qū)別于流行于iOS領(lǐng)域的其他模式,MVVM是另外一種有趣的設(shè)計(jì)模式分歇。具有挑戰(zhàn)性的是它可能會讓你的項(xiàng)目更加依賴像RxSwift這樣的第三方框架傀蓉。MVVM由Microsoft發(fā)明,而且由于他的本地?cái)?shù)據(jù)綁定使得它極為好用职抡。有時(shí)你甚至?xí)羞@種感覺:你通過它與iOS SDK fight葬燎。另外一個(gè)具有挑戰(zhàn)性的方面是團(tuán)隊(duì)的學(xué)習(xí)曲線,這是由于它不同以往的編程方式造成的缚甩。不管如何谱净,它都是對MVC的一次升級,是選擇架構(gòu)時(shí)的一個(gè)有價(jià)值的參考項(xiàng)蹄胰。
Clean Architecture with VIPER and Clean?Swift
Robert C. Martin岳遥,世界級軟件開發(fā)大師,設(shè)計(jì)模式和敏捷開發(fā)先驅(qū)裕寨,敏捷聯(lián)盟首任主席浩蓉,C++ Report 前主編,被后輩程序員尊稱為“Bob大叔”宾袜。
有這樣一個(gè)有趣的引述:VIPER就是當(dāng)你允許java企業(yè)開發(fā)者進(jìn)入iOS領(lǐng)域?qū)l(fā)的一些事情(VIPER is what happens when you allow Java enterprise programmers into the iOS world.)捻艳。VIPER會最大限度的分離關(guān)注點(diǎn)且極易測試,這既符合Uncle Bob 的整潔架構(gòu)思想庆猫。但是认轨,再帶來諸多好處的同時(shí),還存在著一些麻煩月培,比如:太多的模板代碼嘁字、太多不同的部分恩急、可能過度的設(shè)計(jì)。有時(shí)應(yīng)用VIPER的項(xiàng)目看起來可能及其整潔纪蜒,但是當(dāng)你進(jìn)行一個(gè)極小的改變的時(shí)候可能會影響到app的很多層(牽一發(fā)而動全身)衷恭。那它還值得采納嗎?答案是纯续,看上面討論的諸多因素而定随珠。
VIP或者Clean Swift是整潔架構(gòu)的另一端口,這和VIPER有相似之處猬错。從表面上看它看起來挺好的窗看,但是(我)沒有實(shí)際的經(jīng)驗(yàn)。現(xiàn)在有很多不同的低耦合的組件來讓你的應(yīng)用變的更易測試倦炒、更加健壯显沈。Xcode中也有模板綁住你從零開始。
Plugin architecture
還有一種不同的方式析校,就是Eclipse鼓勵的插件架構(gòu)构罗。
首先這種方式解決什么樣的問題,以及為什么我們決定使用它智玻?Well,有這樣一個(gè)項(xiàng)目——用戶并不知道最終產(chǎn)品應(yīng)該長什么樣子芙代。我們有一個(gè)創(chuàng)建靈活的框架的任務(wù)吊奢,使用這個(gè)框架我們可以創(chuàng)建很多由幾個(gè)可組合的模塊組成的不同的應(yīng)用∥婆耄客戶可以用他們來進(jìn)行A/B測試页滚,找出用戶想要的關(guān)鍵特征。例如铺呵,在其中一個(gè)app中包含兩個(gè)模塊裹驰,而在另外一個(gè)app中則包含5個(gè)模塊。菜單是可插拔的片挂,這意味著我們可以輕松地把tiles菜單替換成旋轉(zhuǎn)菜單幻林、導(dǎo)航抽屜或者tab bar。超級靈活音念!最酷的事情是我們可以通過文件對app的外觀和感覺進(jìn)行配置沪饺。這意味著只要后臺的一點(diǎn)小小的改變我們就可以觸發(fā)一個(gè)完全不同的app。
App容器知道如何為app讀取配置信息闷愤、展示模塊整葡。但它不知道應(yīng)該展示那個(gè)模塊,抑或模塊是如何實(shí)現(xiàn)的(原生讥脐,混合)遭居。
各個(gè)模塊是彼此獨(dú)立的啼器,因此開發(fā)者可以完全的進(jìn)行獨(dú)立開發(fā)。為了與各模塊進(jìn)行交互俱萍,開發(fā)者需要在容器中實(shí)現(xiàn)一些必不可少的方法镀首。模塊需要提供擴(kuò)展點(diǎn),在這里你可以注入其他模塊的功能鼠次。
做到這些需要大量使用協(xié)議(protocols)更哄。這個(gè)架構(gòu)能很好的滿足你的需求。然而腥寇,這樣還存在一些挑戰(zhàn)成翩,例如:模塊間的通信難問題和代碼復(fù)用問題(因?yàn)槟K之間是彼此獨(dú)立的)。
Uni-directional data?flow(單向數(shù)據(jù)流)
配合著單向數(shù)據(jù)流使用還有一種有趣的架構(gòu)赦役。受到Redux麻敌、Flux、ReSwift及其他相似架構(gòu)的項(xiàng)目的啟發(fā)掂摔,可以使用單一的數(shù)據(jù)結(jié)構(gòu)展示app的狀態(tài)术羔。在這個(gè)數(shù)據(jù)結(jié)構(gòu)保存著app使用中的的UI和models的狀態(tài)。Views訂閱并對任何的狀態(tài)改變進(jìn)行相應(yīng)的相應(yīng)——他們是觀察者乙漓。只能通過向保存app狀態(tài)的存儲器發(fā)送消息才能改變狀態(tài)级历。通過使用減速器(reducers)展示狀態(tài)的改變。這種類型的架構(gòu)可以更容易的幫助你解釋程序的狀態(tài)叭披。當(dāng)需要恢復(fù)之前的狀態(tài)的時(shí)候(例如:撤銷功能)寥殖,它們的用處的真很大。然而涩蜘,時(shí)間將會告訴你這個(gè)架構(gòu)是否適用于更復(fù)雜的app嚼贡。
Conclusion
應(yīng)該選擇什么樣的架構(gòu)呢?當(dāng)然同诫,并沒有直截了當(dāng)?shù)拇鸢冈敛摺H绻鸢负苊黠@的話就不會出現(xiàn)那么博客和討論了。大家總是能夠選擇最好的(最合適的)其中一個(gè)误窖。誠然叮盘,不同的架構(gòu)在給定的問題領(lǐng)域中可能會有不俗的表現(xiàn)。對于這個(gè)話題你是如何想的贩猎?有沒有一個(gè)完美的app架構(gòu)熊户?