Clean Swift

Clean Swift.png

本文翻譯自 Clean Swift

GitHub Demo

過去兩年,有許多關(guān)于 VIPER 的文章,這是一種在iOS項目中十分流行的架構(gòu)模式捐寥。 如果你對它還不了解,可以看看 這篇文章祖驱。

今天握恳,我想談?wù)劥?VIPER 的另一個選擇 -- Clean Swift

首先捺僻,Clean Swift 和 VIPER 有些相似睡互;然而,在查看它們模塊間交互的方式后陵像,兩者的區(qū)別也顯而易見就珠。在 VIPER 中,模塊交互的基礎(chǔ)是 Presenter醒颖,Presenter 將用戶的請求傳遞到 Interactor 處理妻怎,Interactor 處理完成后將結(jié)果返回給 Presenter,Presenter 把結(jié)果格式化為 View Controller 要展示的形式泞歉,并返回給 View Controller:

VIPER VIP.png

在 Clean Swift 中逼侦,主要的模塊是 View Controller匿辩,InteractorPresenter榛丢,和 VIPER 有點(diǎn)相似:

Clean Swift VIP.png

但與 VIPER 不同的是铲球,在 Clean Swift 中,不同模塊之間的交互是發(fā)生在一個圓形循環(huán)中晰赞。數(shù)據(jù)的傳遞方式基于協(xié)議(VIPER 也是如此)稼病,采用協(xié)議的好處是 允許我們在需要的時候?qū)⒋讼到y(tǒng)中的一個組件,替換為另一個遵守了相同協(xié)議的對象掖鱼。它們之間的交互處理過程通常像這樣:用戶點(diǎn)擊一個按鈕然走,View Controller 創(chuàng)建一個攜帶相關(guān)信息的對象,把它送給 Interactor戏挡。Interactor 則根據(jù)業(yè)務(wù)邏輯啟動特定的場景芍瑞,得到結(jié)果并傳給 Presenter。Presenter 將結(jié)果格式化為需要展示的樣式褐墅,再發(fā)給 View Controller 展示拆檬。讓我們來仔細(xì)看看 Clean Swift 的各個模塊吧。

View (View Controller)

View Controller 負(fù)責(zé)配置所有視圖相關(guān)的屬性(與 VIPER 相似)妥凳,像顏色竟贯、UILabel 的樣式或者布局。因此猾封,在 Clean Swift中的每個 UIViewController 都實(shí)現(xiàn)了一個特定的輸入?yún)f(xié)議(DisplayLogic)來展示數(shù)據(jù) 或 展示用戶的操作結(jié)果澄耍。

Interactor

Interactor 包含了所有的業(yè)務(wù)邏輯。它接收來自 View Controller 的用戶的操作事件及參數(shù)(例如:輸入框文本的變化 或 按鈕的點(diǎn)擊)晌缘。這些事件被定義在 Interactor 的輸入?yún)f(xié)議中(BusinessLogic)齐莲。在處理完成后,Interactor 將結(jié)果傳遞給 Presenter磷箕,Presenter 會格式化結(jié)果并傳遞給 View Controller选酗。

Clean Swift 中,Interactor 只會接收來自 View Controller 的請求岳枷。然而芒填,而在 VIPER 中,這些請求將通過 Presenter 作為中間層來傳遞空繁。

Presenter

Presenter 會準(zhǔn)備好展示給用戶的數(shù)據(jù)并輸出到 View Controller殿衰。這些輸出的結(jié)果會遵守 View Controller 的輸入?yún)f(xié)議(DisplayLogic)。例如盛泡,Presenter 可以改變文本的格式闷祥,將枚舉的顏色轉(zhuǎn)換為 RGB,等等傲诵。

Worker

為了避免繁瑣的業(yè)務(wù)細(xì)節(jié)和重復(fù)的邏輯導(dǎo)致 Interactor 過于復(fù)雜和龐大凯砍,你可以添加額外的 Worker 模塊箱硕。對于簡單的項目來說,Worker 并非是必要的選擇悟衩,但是在復(fù)雜的場景下剧罩,它將為 Interactor 分擔(dān)部分任務(wù)。例如:Worker 可以實(shí)現(xiàn)與數(shù)據(jù)庫交互的邏輯座泳,特別是當(dāng)程序的不同地方使用相同的查詢時惠昔。

Router

Router 的職責(zé)是在不同界面之間跳轉(zhuǎn)和傳遞數(shù)據(jù)。它引用了 View Controller钳榨,這是因為在 iOS 系統(tǒng)上界面跳轉(zhuǎn)一直是 View Controller 的職責(zé)舰罚。如果你是用 segues纽门,則可以通過在 PrepareForSegue 方法中調(diào)用 Router 方法來簡化界面跳轉(zhuǎn)的初始化薛耻,因為 Router 已經(jīng)知道如何傳輸數(shù)據(jù)并在沒有 Interactor / Presenter 的情況下完成此操作。使用 Interactor 中實(shí)現(xiàn)的每個界面的 DataStore 協(xié)議傳遞數(shù)據(jù)赏陵,該協(xié)議還限制了從路由器訪問界面內(nèi)部數(shù)據(jù)的能力饼齿。

Models

Models 是純數(shù)據(jù)結(jié)構(gòu),它描述了在不同模塊間數(shù)據(jù)傳遞的信息蝙搔。業(yè)務(wù)邏輯(BusinessLogic)的每個實(shí)現(xiàn)函數(shù)都有自己的 Model缕溉。Request 用于從 View Controller 向 Interactor 發(fā)送請求。Response 是 Interactor 對 Presenter 的響應(yīng)吃型。ViewModel 描述了從 Presenter 傳輸?shù)?View Controller 進(jìn)行顯示的數(shù)據(jù)证鸥。

Example

讓我們用一個簡單的 例子 來研究一下這個架構(gòu)。這是一個簡化表示 ContactBook 的應(yīng)用程序勤晚,但它足以讓我們理解 Clean Swift 的基礎(chǔ)枉层。這個 app 包括聯(lián)系人列表,以及添加和編輯聯(lián)系人的功能赐写。

每個 View Controller 包含了一個實(shí)現(xiàn)了業(yè)務(wù)邏輯協(xié)議(BusinessLogic)的對象鸟蜡,叫做 Interactor,也包含一個 Router 對象挺邀,它實(shí)現(xiàn)了界面間數(shù)據(jù)傳輸和跳轉(zhuǎn)的協(xié)議揉忘。

你可以在 View Controller 中用一個單獨(dú)的私有方法中配置 Interactor 和 Router;或者端铛,有些開發(fā)者認(rèn)為 View Controller 不需要參與此配置泣矛,你也可以創(chuàng)建一個 Configurator 單例來將這部分的配置代碼抽離出 View Controller,并且 Configurator 不應(yīng)訪問 View Controller 中的其他部分代碼禾蚕。Configurator 類并不存在于 Uncle Bob's clean architecture 的描述中您朽,也不出現(xiàn)在經(jīng)典的 VIPER 架構(gòu)中。在添加聯(lián)系人的界面使用 Configurator 如下所示:

Configurator 包含一個配置方法夕膀,與 View Controller 中的配置方法相同虚倒。

[譯者注]:Clean Swift 已不使用 Configurator 了美侦,取而代之的是在 View Controller 中使用 setup() 來配置]

詳見:Zero configuration

View Controller 實(shí)現(xiàn)中的另一個重點(diǎn)是 prepareForSegue() 方法中的代碼:

細(xì)心的讀者可能已注意到 Router 被要求遵守 NSObjectProtocol。這樣做是為了在使用 segue 時魂奥,我們可以使用此協(xié)議的標(biāo)準(zhǔn)方法進(jìn)行路由菠剩。為了支持這種簡單的重定向,segue 標(biāo)識符的命名必須與 Router 方法名稱的結(jié)尾完全相同耻煤。例如:點(diǎn)擊 cell 跳轉(zhuǎn)查看聯(lián)系人界面具壮,在 Storyboard 中有一個 segue 與之關(guān)聯(lián),它的標(biāo)識符是 “ViewContact”哈蝇,那么棺妓,在 Router 中就應(yīng)該有一個相應(yīng)的 “routeToViewContact()” 方法。

Interactor 請求數(shù)據(jù)用來展示看起來也很容易:

讓我們看一下 Interactor炮赦。Interactor 實(shí)現(xiàn)了 ContactListDataStore protocol怜跑,這個協(xié)議描述了存儲和訪問的數(shù)據(jù)。在我們的例子中吠勘,它只是一個聯(lián)系人數(shù)組性芬,用 getter 方法限制了它在 Router 中不能被其他 模塊修改。

這是我們聯(lián)系人列表 業(yè)務(wù)邏輯協(xié)議的實(shí)現(xiàn):

它從 ContactListWorker 接收 聯(lián)系人數(shù)據(jù)剧防。在這個例子中植锉,Worker 負(fù)責(zé)數(shù)據(jù)的加載方式。Worker 可以訪問第三方服務(wù)峭拘,例如俊庇,決定是從緩存中獲取數(shù)據(jù)還是從網(wǎng)絡(luò)下載。Interactor 收到數(shù)據(jù)之后鸡挠,發(fā)送一個 response 給 Presenter 來準(zhǔn)備要展示的數(shù)據(jù)辉饱。Interactor 包含對 Presenter 的引用,用于實(shí)現(xiàn)這個功能宵凌。

Presenter 只實(shí)現(xiàn)了一個協(xié)議 -- ContactListPresentationLogic鞋囊,在我們的例子中,它將聯(lián)系人的姓名和姓氏的首字母改為大寫瞎惫,然后形成 DisplayedContact 的數(shù)據(jù)模型溜腐,并將其傳遞給 View Controller 以顯示。

至此瓜喇,VIP 循環(huán)就完成了挺益,View Controller 展示數(shù)據(jù),實(shí)現(xiàn)協(xié)議 ContactListDisplayLogic 的方法。

以下是顯示聯(lián)系人的數(shù)據(jù)模型:

在這種情況下,查詢不包含查詢參數(shù)贫奠,因為它只是一個典型的聯(lián)系人列表谋竖。但是,如果聯(lián)系人列表界面包含過濾等限制参滴,則可以將過濾參數(shù)添加到此查詢請求中瘸羡。Interactor 的 response model 包含了必要的聯(lián)系人數(shù)組代赁,ViewModel 也由用于展示的 DisplayedContact 數(shù)組組成甘耿。

為什么要使用 Clean Swift 呢踊兜?

讓我們思考一下這個架構(gòu)的優(yōu)缺點(diǎn)。

首先佳恬,Clean Swift 有模板代碼使得我們很容易就可以創(chuàng)建一個模塊捏境。這些模板代碼可以針對各種體系結(jié)構(gòu)編寫,但是當(dāng)它們開箱即用時毁葱,可以為你節(jié)省幾個小時的時間垫言。

第二,這個架構(gòu)倾剿,包括 VIPER筷频,都是容易測試的。任何模塊都可以通過 mock 輕松替換掉柱告,因為每個模塊的功能都在協(xié)議中描述截驮。當(dāng)我們同時實(shí)現(xiàn)業(yè)務(wù)邏輯和相關(guān)測試時笑陈,我們已經(jīng)在使用測試驅(qū)動開發(fā)(TDD)际度。由于每個邏輯案例的都是由協(xié)議定義的,因此可以先編寫一個確定其行為的測試涵妥,然后直接實(shí)現(xiàn)該方法乖菱。

第三,Clean Swift 有單向的數(shù)據(jù)處理和決策流程(這是和 VIPER 相對的)蓬网,始終只有一個循環(huán) View Controller --> Interactor --> Presenter --> View Controller窒所,這簡化了重構(gòu)。因為大部分的時候帆锋,將會修改更少的實(shí)體吵取。因此,使用 Clean Swift 架構(gòu)時锯厢,具有經(jīng)常更改邏輯的項目更容易重構(gòu)皮官。在 Clean Swift 中,可以通過兩種方式分離實(shí)體:

  • 通過在聲明輸入和輸出協(xié)議中隔離組件
Clean Swift Isolation.png
  • 通過使用結(jié)構(gòu)隔離功能实辑,并將數(shù)據(jù)封裝到單獨(dú)的 Request/Response/ViewModel 中捺氢。每個功能都有其邏輯,并在同一過程中進(jìn)行控制剪撬,不會與其他功能重疊摄乒。

Clean Swift 不應(yīng)該用于沒有長遠(yuǎn)眼光的小型項目,也不應(yīng)該用于原型。相反馍佑,長期的項目和具有大量業(yè)務(wù)邏輯的項目非常適合這種架構(gòu)斋否。當(dāng)項目為 Mac OS 和 iOS 兩個平臺開發(fā)(或者有此計劃)時,使用 Clean Swift 非常方便拭荤,因為大部分代碼模塊(除 View Controller 外如叼,有時也會包括 Router 在內(nèi))可以不用修改就被重用。

不當(dāng)之處穷劈,還請指正

附:Clean Swift 官網(wǎng)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末笼恰,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子歇终,更是在濱河造成了極大的恐慌社证,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件评凝,死亡現(xiàn)場離奇詭異追葡,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)奕短,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進(jìn)店門宜肉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人翎碑,你說我怎么就攤上這事谬返。” “怎么了日杈?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵遣铝,是天一觀的道長。 經(jīng)常有香客問我莉擒,道長酿炸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任涨冀,我火速辦了婚禮填硕,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘鹿鳖。我一直安慰自己扁眯,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布栓辜。 她就那樣靜靜地躺著恋拍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪藕甩。 梳的紋絲不亂的頭發(fā)上施敢,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天周荐,我揣著相機(jī)與錄音,去河邊找鬼僵娃。 笑死概作,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的默怨。 我是一名探鬼主播讯榕,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼匙睹!你這毒婦竟也來了愚屁?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤痕檬,失蹤者是張志新(化名)和其女友劉穎霎槐,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體梦谜,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡丘跌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了唁桩。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片闭树。...
    茶點(diǎn)故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖荒澡,靈堂內(nèi)的尸體忽然破棺而出报辱,到底是詐尸還是另有隱情,我是刑警寧澤仰猖,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布捏肢,位于F島的核電站,受9級特大地震影響饥侵,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜衣屏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一躏升、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧狼忱,春花似錦膨疏、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至窘俺,卻和暖如春饲帅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工灶泵, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留育八,地道東北人。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓赦邻,卻偏偏與公主長得像髓棋,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子惶洲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容