Rails 缺失的部分(1):Interactors

在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上得到一些启发。

收藏 评论

关于作者:geekerzp

Ruby Geek (新浪微博:@geekerzp) 个人主页 · 我的文章

相关文章

可能感兴趣的话题



直接登录
跳到底部
返回顶部