本文由CocoaChina譯者@WhistlingArrow翻譯
原文:Ziggurat iOS App Architecture
今年六月,我做了一場關於避免臃腫的ViewController的演講,用Swift講解了一種采用“單向數據流”的架構模式。當時並沒有發布相關的博客,甚至沒有給這個架構起個名字。現在兩者都有了。首先介紹一下Ziggurat:它是一種通過不可變的視圖模型和單向數據流來實現的分層的、易測試的架構模式。
這個架構的名字Ziggurat是根據階梯狀的金字塔得來的。像金字塔的階梯一樣,數據以數據流的形式單向地沿著App的層級傳遞,並逐層縮小。單向不可變的數據流可以減少認知負載,同時使類也變得更加瘦小。和Ziggurat相比,典型的MVC架構顯得缺少了一些指導性,對數據和狀態的操作可能散落各處,包括ViewController。
Ziggurat結合了大量討論和文章中的概念,相關內容列在了下文中更多參考部分。(劇透一下:這個架構的靈感來源於之前Facebook關於React Native的討論,並且它和Flux也很相似)。這裡提及的這些架構模式與MVC相比都是一種更好的選擇。
下面將具體介紹Ziggurat:闡述它的背景,權衡分析優缺點,定義它的組件,並通過一個例子來介紹它的用法。
Ziggurat是MVC的替代選擇。MVC只是簡單地將每部分的責任分離開,但並沒有將每個角色和責任分離的很清晰適當。下面是MVC帶來的常見問題:
ViewController負責處理數據、管理I/O、請求API、包含了Model類,以及其他超出它的范圍的任務:管理視圖及UI事件。
誰應該處理數據?什麼時候處理數據?如果不經過精心設計,那麼答案將是誰都可以處理、什麼時候都能處理。沒有搞清這些會造成一些列的連鎖效應。
沒有采用單一責任原則時會使界限變得不明確且不易發現。
在實踐中,由MVC缺少隔離導致的App沒有一種清晰地設計模式使得測試和調試bug變得非常困難。對一份代碼,不應該讓工程師們通過將一個App的設計模式和數據模型做逆向工程的方式來提高工作效率。
這個例子使用了這個架構模式,並且所有的組件都有說明文檔
上面例子的說明文檔提供text格式文件
我的臃腫的ViewController演講
Square工程師Kat Hawthorne的關於單向數據流的演講和幻燈片,下面是我引用其中的一張:
一個外部事件觸發(例如用戶輸入)
ViewController通知服務器它收到了用戶輸入
服務器開始解析或確認此次輸入並可能對改變其狀態(只有服務器能改變其狀態)。然後調用signal()方法
signal()方法通知渲染器開始更新
渲染器通知展示者,並生成一個View Model(服務器不會打斷循環渲染過程)
渲染器將View Model 傳遞給View Controller
渲染循環等待下一個外部事件觸發
Ziggurat架構模式解決了MVC App面臨的常見問題:
多層的架構模式為所有功能提供了清晰的根節點,從而避免了臃腫的View Controller
減少了無狀態性和易變性:服務器層封裝了易變的發生(將它限制在主線程上),並且展現者和渲染都是無狀態的和單向的。使用依賴注入代替了公用狀態和全局單例。
更加瘦小的分層使責任分布得更均勻
Ziggurat以很多方式優化了我們的生活,它使小團隊的工程師們(從沒有經驗到幾年經驗)可以在很短的期限內完成任務。
首先,它通過給工程師在面對新代碼庫時提供指導和保護性措施的方式,清晰地定義了App的層級和角色。新工程師對它的上手時間很短,因為每個組件都定義的很明確並且符合人們的直觀感覺。其次,Ziggurat模式非常易於測試。例如,可以用View Model層來將數據以目標結構輸出。這點在MVC中是無法實現的。
單向數據流避免了MVC。不可變類型,更小的類(良好的結構),更多的分層減少了工程師們的精力消耗。它可以很簡單地以輕松方式來運行你的App,因為View層中不必包含業務邏輯;所有的業務邏輯都是可測試的而不要View層;View的狀態可以通過View Model層在某一時間點的快照來重新創建。最後,我們發現這使得App變得非常輕便。多虧了這種分層的方式和依賴注入,可以輕松實現代碼移植。
Ziggurat會使一些方面變得更加困難,比如動畫。我們建議在不需要很多動畫的App中使用Ziggurat。在View Controller中實現的動畫會在將來調用有關更新的方法時遭到破壞,如果不小心控制這點的話會造成界面的閃動。在Flux中,使用了React Native來解決這個問題。
此外,雖然提供了編譯期的檢查,在逐層傳遞數據時還是會帶來一些樣板代碼。並且信號數據在傳輸時會存在潛在性的瓶頸。與React不同,屬性變量不是KVO的;而是通過View Model的方式來更新屬性。這裡可能需要做一些優化,比如減少一些不需要的渲染或是調整一下你的View Model。
依賴注入需要小心地設計。我們最初的方法就導致了一張很大的由類組成的網。我們用了一些循環的依賴,導致最後不得不重構。
在新項目中有機會嘗試新的想法。MVC存在很多明顯的劣勢。與此同時,一些主題的討論比如Flux、React Native、廣泛地使用Swift的值類型等正在業界形成了一股勢頭。
我們混合使用了上面的想法,用Swift寫了程序。我們感到很興奮對於使用單向數據流帶來的效果:可測試性、依賴注入、輕量級的View Controller、值類型的View Model。
這是關於Ziggurat權衡優缺點的分析,但到目前為止它仍可以很好地服務於我們。我們希望你在下一個項目中考慮采用一些這些想法(或者全部采用!),尤其是在你打算使用MVC之前。
我看了早期的關於React的視頻,它們是Ziggurat靈感的直接來源。Flux和React Native使用了相同的概念並且是通過JavaScript實現的。
Flux是一種很強健的,以action為中心的設計模式,並且可以結合React Native一起使用。Ziggurat在涉及動畫較少的App中使用會非常容易,(Flux同樣存在這個問題,但React Native中解決了這個問題)
我們希望你在下列文章中找到Ziggurat模式的有益之處,以及一個可以代替MVC的更好方案:
OS Architecture Patterns
MVVM is Not Very Good
Introduction to MVVM 和 MVVM in Swift
Controlling Complexity in Swift
Introducing React Native
Flux (and talk)
Architecting iOS Apps with VIPER
Simple static table views for iOS in Swift
感謝Ruby Chen的插圖
更多譯者翻譯文章,請查看:http://www.cocoachina.com/special/translation/
本文僅用於學習和交流目的,轉載請注明文章譯者、出處和本文鏈接。
感謝博文視點對本期活動的支持