【原文】https://medium.com/@martinmitrevski/pragmatic-ios-app-architecture-f7d6334fd8e4
關(guān)于iOS App架構(gòu)的文章有很多,解決方案有很多,其中也不乏設(shè)計(jì)精巧的架構(gòu)。但是沒(méi)有一種架構(gòu)適合所有的場(chǎng)景。But,如何選擇、取舍呢?下面的評(píng)估中有幾條參考規(guī)范。
Evaluating architecture
app中的每個(gè)模塊都應(yīng)該精心的組織并解耦。模塊之間不需要知道彼此的內(nèi)部詳細(xì)情況。
架構(gòu)要表達(dá)出項(xiàng)目所屬的商業(yè)領(lǐng)域。這是新加入成員能夠快速?gòu)拇a中獲取到的信息。對(duì)于維護(hù)和擴(kuò)展一個(gè)產(chǎn)品,架構(gòu)能進(jìn)行“自我介紹”是至關(guān)重要的。特別是,在不斷地往里面添加人手的時(shí)候。
可擴(kuò)展性。是否容易往里面添加新的特性。擁有一個(gè)優(yōu)雅的解決方案可以在未來(lái)幫你節(jié)省大量的時(shí)間和金錢(qián)。
架構(gòu)是否滿(mǎn)足商業(yè)領(lǐng)域的需求。app是以大量數(shù)據(jù)驅(qū)動(dòng)的?還是有大量需要用戶(hù)填寫(xiě)的表單?app的復(fù)雜程度,是只有五個(gè)頁(yè)面還是有50個(gè)頁(yè)面?
開(kāi)發(fā)團(tuán)隊(duì)的工作效率。團(tuán)隊(duì)是否可以快速的理解新的架構(gòu)和不懂得概念?是否可以無(wú)障礙的獨(dú)立進(jìn)行開(kāi)發(fā)工作?可以想象你的架構(gòu)中只有一個(gè)storyboard,在多人協(xié)作并且都編輯storyboard的情況下,合并代碼的時(shí)候可能會(huì)掉進(jìn)坑里!
測(cè)試是選擇架構(gòu)的另一個(gè)重要因素。那個(gè)關(guān)鍵模塊是我們想要測(cè)試的?只對(duì)值得測(cè)試的部分進(jìn)行測(cè)試,不要為了代碼覆蓋率(code coverage)而進(jìn)行測(cè)試。你可能有90%的代碼覆蓋率但是卻沒(méi)有覆蓋到至關(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)目,但銷(xiāo)售人員追求的卻是快速。每個(gè)人都有自己的關(guān)注點(diǎn),我們需要有意識(shí)的在兩只之間的進(jìn)行平衡。低劣的產(chǎn)品不會(huì)持久,但是遲來(lái)的產(chǎn)品也會(huì)被市場(chǎng)所淘汰。
因此,實(shí)用、中立(neutrality)、大局觀,正是這些方面驅(qū)動(dòng)著我決定采用什么樣的架構(gòu)。
The good old?MVC (經(jīng)典架構(gòu) - MVC)
相當(dāng)困惑,為什么那么多人都放棄實(shí)用MVC模式?有人說(shuō)它代表著復(fù)雜笨重的View Controllers,不適合大型項(xiàng)目。但是我們有很多概念和模式可以幫助減小View Controllers的大小。比如:?delegation(代理), composition, dependency injection, protocols(協(xié)議), pure functions(唇方法), service / utility classes, navigation centers等。這些技術(shù)手段使得測(cè)試并不是那么的困難。

Going reactive
當(dāng)然,這還要看app的類(lèi)型,像facebook這種數(shù)據(jù)驅(qū)動(dòng)的app就不適合使用MVC,fb里面有太多的cells和內(nèi)容,使用經(jīng)典的MVC幾乎不太可能。一個(gè)帶有數(shù)據(jù)流的更加被動(dòng)的方案更適合這種情況(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)性的是它可能會(huì)讓你的項(xiàng)目更加依賴(lài)像RxSwift這樣的第三方框架。MVVM由Microsoft發(fā)明,而且由于他的本地?cái)?shù)據(jù)綁定使得它極為好用。有時(shí)你甚至?xí)羞@種感覺(jué):你通過(guò)它與iOS SDK fight。另外一個(gè)具有挑戰(zhàn)性的方面是團(tuán)隊(duì)的學(xué)習(xí)曲線,這是由于它不同以往的編程方式造成的。不管如何,它都是對(duì)MVC的一次升級(jí),是選擇架構(gòu)時(shí)的一個(gè)有價(jià)值的參考項(xiàng)。

Clean Architecture with VIPER and Clean?Swift
Robert C. Martin,世界級(jí)軟件開(kāi)發(fā)大師,設(shè)計(jì)模式和敏捷開(kāi)發(fā)先驅(qū),敏捷聯(lián)盟首任主席,C++ Report 前主編,被后輩程序員尊稱(chēng)為“Bob大叔”。
有這樣一個(gè)有趣的引述:VIPER就是當(dāng)你允許java企業(yè)開(kāi)發(fā)者進(jìn)入iOS領(lǐng)域?qū)?huì)引發(fā)的一些事情(VIPER is what happens when you allow Java enterprise programmers into the iOS world.)。VIPER會(huì)最大限度的分離關(guān)注點(diǎn)且極易測(cè)試,這既符合Uncle Bob 的整潔架構(gòu)思想。但是,再帶來(lái)諸多好處的同時(shí),還存在著一些麻煩,比如:太多的模板代碼、太多不同的部分、可能過(guò)度的設(shè)計(jì)。有時(shí)應(yīng)用VIPER的項(xiàng)目看起來(lái)可能及其整潔,但是當(dāng)你進(jìn)行一個(gè)極小的改變的時(shí)候可能會(huì)影響到app的很多層(牽一發(fā)而動(dòng)全身)。那它還值得采納嗎?答案是,看上面討論的諸多因素而定。

VIP或者Clean Swift是整潔架構(gòu)的另一端口,這和VIPER有相似之處。從表面上看它看起來(lái)挺好的,但是(我)沒(méi)有實(shí)際的經(jīng)驗(yàn)?,F(xiàn)在有很多不同的低耦合的組件來(lái)讓你的應(yīng)用變的更易測(cè)試、更加健壯。Xcode中也有模板綁住你從零開(kāi)始。
Plugin architecture
還有一種不同的方式,就是Eclipse鼓勵(lì)的插件架構(gòu)。
首先這種方式解決什么樣的問(wèn)題,以及為什么我們決定使用它?Well,有這樣一個(gè)項(xiàng)目——用戶(hù)并不知道最終產(chǎn)品應(yīng)該長(zhǎng)什么樣子。我們有一個(gè)創(chuàng)建靈活的框架的任務(wù),使用這個(gè)框架我們可以創(chuàng)建很多由幾個(gè)可組合的模塊組成的不同的應(yīng)用??蛻?hù)可以用他們來(lái)進(jìn)行A/B測(cè)試,找出用戶(hù)想要的關(guān)鍵特征。例如,在其中一個(gè)app中包含兩個(gè)模塊,而在另外一個(gè)app中則包含5個(gè)模塊。菜單是可插拔的,這意味著我們可以輕松地把tiles菜單替換成旋轉(zhuǎn)菜單、導(dǎo)航抽屜或者tab bar。超級(jí)靈活!最酷的事情是我們可以通過(guò)文件對(duì)app的外觀和感覺(jué)進(jìn)行配置。這意味著只要后臺(tái)的一點(diǎn)小小的改變我們就可以觸發(fā)一個(gè)完全不同的app。

App容器知道如何為app讀取配置信息、展示模塊。但它不知道應(yīng)該展示那個(gè)模塊,抑或模塊是如何實(shí)現(xiàn)的(原生,混合)。
各個(gè)模塊是彼此獨(dú)立的,因此開(kāi)發(fā)者可以完全的進(jìn)行獨(dú)立開(kāi)發(fā)。為了與各模塊進(jìn)行交互,開(kāi)發(fā)者需要在容器中實(shí)現(xiàn)一些必不可少的方法。模塊需要提供擴(kuò)展點(diǎn),在這里你可以注入其他模塊的功能。
做到這些需要大量使用協(xié)議(protocols)。這個(gè)架構(gòu)能很好的滿(mǎn)足你的需求。然而,這樣還存在一些挑戰(zhàn),例如:模塊間的通信難問(wèn)題和代碼復(fù)用問(wèn)題(因?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訂閱并對(duì)任何的狀態(tài)改變進(jìn)行相應(yīng)的相應(yīng)——他們是觀察者。只能通過(guò)向保存app狀態(tài)的存儲(chǔ)器發(fā)送消息才能改變狀態(tài)。通過(guò)使用減速器(reducers)展示狀態(tài)的改變。這種類(lèi)型的架構(gòu)可以更容易的幫助你解釋程序的狀態(tài)。當(dāng)需要恢復(fù)之前的狀態(tài)的時(shí)候(例如:撤銷(xiāo)功能),它們的用處的真很大。然而,時(shí)間將會(huì)告訴你這個(gè)架構(gòu)是否適用于更復(fù)雜的app。
Conclusion
應(yīng)該選擇什么樣的架構(gòu)呢?當(dāng)然,并沒(méi)有直截了當(dāng)?shù)拇鸢?。如果答案很明顯的話就不會(huì)出現(xiàn)那么博客和討論了。大家總是能夠選擇最好的(最合適的)其中一個(gè)。誠(chéng)然,不同的架構(gòu)在給定的問(wèn)題領(lǐng)域中可能會(huì)有不俗的表現(xiàn)。對(duì)于這個(gè)話題你是如何想的?有沒(méi)有一個(gè)完美的app架構(gòu)?