Rails 缺失的部分(1):Interactors

geekerzp發表於2014-06-11

在Grouper,我們是Rails的長期使用者,像其他在紐約的敏捷團隊RapGenius和Kickstarter一樣。它容易使用並且可以有效提高開發人員的效率。

然而,人們開始注意到它的缺點–一旦程式碼量超過幾千行,測試套件會變的緩慢並且框架載入時間會顯著增加。

一些沒有幫助的Rails特性鼓勵使用者少用設計模式,這通常會導致高度耦合的程式碼,以及緩慢不可維護的測試套件。我們意識到沒有必要非得這樣做。

Rails或許是敏捷開發的最完美的工具,但是對於中型或大型程式它並不是最有效的。我們通過三條準則解決了這些問題,我們相信這些概念可以成為任何高階rails開發的一部分,它們是:Interactors,Policies,Presents。

第一部分 — Interactors

現在流行的趨勢是在rails程式碼中,在ActiveRecord的models資料夾中,將大部分業務邏輯程式碼放在一個巨大的類中。這通常會暗示一個類擁有太多的職責,一個巨大的公共API,以及僅僅為了實現複雜的關聯關係的物件功能的方法。

一個重要的因素是ActiveRecord callbacks;before_save鉤子函式在一個類中修改其他物件的狀態。這是一個嚴重的問題,因為在處理我們關心的物件之前必須獲取所有這些其他物件。在測試的時候,將會變得相當緩慢(因為這些物件通常要從資料庫中獲取),回撥函式和長長的方法呼叫鏈的執行也會讓這個過程變得艱難。

解決的方法是”Interactors”的準則(通常被叫做”Service Objects”)。Interactors要求應用的核心儲存在一系列純粹的ruby物件中,儲存主要的應用邏輯,使ActiveRecord類作為資料持久化的介面層。通過檢視Interactors的名字,你可以清楚的明白應用的功能;例如,SignUpBookGrouperAssignBarForGrouper等等。其他的類,像MemberBar僅僅是為了驗證和儲存屬性,例如name,location和date。

(作為背景 — 組織者組織名為”Groupers”的活動,在酒吧中進行3對3暢飲,這是一個很有趣的活動。)

Interactor是一個輕量級的gem,提供了一系列方便的方法,例如success?failure?,因此你的控制器看起來像下面這樣:

你的Interactor看起來像這樣:

這種方式優點是很多的,同時在程式碼複雜性和測試速度方面。舉例來說,你的測試現在會明顯加快,因為你幾乎不需要去讀取資料庫,同時你可以獨立的去測試每個ActiveRecord模型而不需要擔心它的關聯。Interactors可以使用虛擬物件(doubles)而不去使用ActiveRecord模型(models),同時,測試套件甚至沒有必要去載入Rails。

你的控制器測試看起來像下面這樣:

通過所有的ActiveRecord資料庫呼叫都在stub中進行,測試執行時間從之前的4,5秒左右降到了0.15秒左右。

另外,這會使你的應用變得更加簡單明瞭,因為每一個類將具有單獨的作用,例如,Bar類知道酒吧的位置和它開放的時間。它不需要知道Grouper,有關於將哪一個Bar分配哪一個Grouper的複雜邏輯安全的儲存在AssignBarForGrouperInteractor中。

最後,如果你保持Interactors短小並且具有單個目的性,你可以組合多個Interactors完成更加複雜的邏輯。這種混合匹配的方式可以有效降低你的程式碼的耦合度,並且可以讓你在必要的時候複用操作。

總結

很明顯,你需要去選擇合適的方式去解決你所遇到的問題,沒有一種萬能的模式去解決所有的問題,上面提到的準則或許在一個15分鐘快速建立部落格的應用中並不合適。我們非常反對Rails建它作為初學者的入門應用,並且建議對所有的情況採用相同的方式。一旦程式碼開始變得複雜,你將對你解決問題的方式沒有把握。我們建議使用Interactors作為解決問題的方式。

這個系列的下一部分, Policy Objects可以有效簡化控制器。

如果你對Rails設計模式和最佳實踐感興趣,請關注我們的職位,我們正在招聘。

你也可以在hacker news上得到一些啟發。

相關文章