DHH 是怎么组织他的 Controller 代码的

2016-12-08 10:27:46来源:作者:人点击

在最近的 Full Stack 访谈里DHH 解释了他在最新的 Basecamp 里面是怎么组织 Rails 的 Controller 代码的。下面是他的原话:

当我每次创建一个新的 Controller 的时候,都按照 REST 作为最基本的准则来组织代码。每次我对自己的 Controller 代码都不是很满意,因为 Controller 太少了,每个 Controller 承载的东西太多了。

所以我们在 Basecamp 3 中,我们每次都把 Controller 拆分成好几个子 Controller 让代码更加合理。比如说一些 filter ,就像在这个场景里面处于某个状态。如果你有很多的 filter, 并且处于不同的状态,有时候我们需要抽出这些状态代码到放到一个新的 controller 里面去。

当我有倾向想往一个 controller 里面加一个方法,但这个方法并不是 REST 默认的 5 个方法的时候,正确的做法应该是增加一个新的 Controller,然后调用它。

让我们举个例子说明下,你有一个InboxController,有个 Index 方法来展示所有的 Inbox 里面的 email,并且你有可能想显示 pending 的所有 email,这时候你可能会在 InboxController 里面增加 pending 方法如下:

这是一个非常常见的模式吧? 但我会用另外一个模式来改变这样的写法。

我会说不不不,应该增加一个新的 controller 叫做Inboxes::PendingsController,只有一个 index 方法。

这样的话,每个 controller 都有自己的范围。所以我们扩散了很多的 controller,尤其是这种命名空间的 controller。所以我们有一个 MessagesController,也有可能有Messages::DraftsController 和Messages::TrashesController,类似这样的子 controller 会很多。这种重构是一个巨大的成功 。

所以 DHH 的意思是一个 controller 应该只用默认的 CRUD 方法,index,show,new,edit,create,update,destroy, 任何其他的动作应该新建一个新的controller,它默认有自己的 CRUD 方法。

我的思考

从现在开始下面是我的个人想法,有不一样的意见是正常的。

不管怎样,我很高兴从 DHH 那里学到了组织 controller 的一个方法,已经使用了超过 1 年了。他提到的例子是 filter 也许过于复杂,一般的 filter 就像查询参数一样的 (GET /inboxes?state=pending),这种简单的处理逻辑,我并不会使用新的 controller ,而复杂的有很多 action 的话,我会像他那样处理。

但是总体来说我是同意分割 controller 的观点的,是基于下面几个原因:

鼓励写成更加简单的代码

使用这种方法,你可以写出你想要的尽可能多的 controller。

一般的 controller 里面的 CRUD 方法比较简单,无需抽出方法到新的 controller,但如果遇到一些复杂的 CRUD 方法,该怎么处理呢? 我们还是应该把这些方法仍然放到自己的 controller 里面。

例如下面是我曾经写过一个最复杂的 controller 代码,这个方法是用来购买我们的产品的 API 处理。

这里面仅有一个 create 方法,没有做任何抽象到 model 里面,也没有 service, observer。所有的东西都在这个 controller 里面。没有必要跳到不同的文件里面去理解代码,在这个文件里面一目了然。

这时候可能会有人问,你把所有处理都放这个里面,有多少行代码啊?是不是非常长?不,仅仅 144 行。

这是我们最复杂的 controller,按理说我们可以把这个代码分块处理,但是我们看起来还不错。我们其他的 controller 就很简单,基本是 6 行到 103 行之间,平均每个 controller 只有 15 行代码。(我们已经有了 150 多个 controller)

让你的代码更加统一规范

controller 里面只有 CRUD 方法的话是一件非常酷的事情。看到这样的代码,你就不会有更多的疑问和猜测,或者发现了一个奇怪的 action。

我喜欢统一的代码风格,约定优于配置,这也是我喜欢 Rails 框架的重要原因之一。所有的事都按照统一的风格,这样我们可以取得更快的进展在业务方面。

从理论上来说,这也意味着,你可以从一个代码库移动到另一个并且在很短的时间内做到100%的生产效率。在实际项目中,人们遇到的更多是可怕的 Rails 应用,有的使用了 Observer, 有的也可能使用了trailblazer, 还有使用其他工具,也有使用自己设计的框架的。

所有的这些都是因为人们觉得代码感到不安不满意,所以他们寻找额外的工具,伙计们,最好的解决方案就是分割你的 controller, 使用默认的 CRUD,非常简单,初级程序员看起来都非常友好。

Rails 的官方文档也提到你应该经常使用 resourceful 的路由,也就是 CRUD 和RESTful 的路由,他可能在提醒你加上 CRUD 以外的方法不是很 Rails Way。所以分割 controller 是一个很好的主意。

可以让你以 RESTFUL 思考问题

很多人喜欢 REST, 是因为 REST 统一并且简单,一旦理解了一个 RESTFUL , 理解另外一个会觉得更加容易。理论上是这样,但是各种业务逻辑处理不一样,你必须去理解它,有可能会遇到这样的业务逻辑:你支付钱到 stripe, 你发短信给别人,从 github 那边获取一个项目。

你应该深入考虑每个业务问题,然后使用 REST 的方式来实现它,例如:付款应该是创建一个支付,余额增加资金应该是在余额中创建了一个资金,一开始你可能会觉得有点奇怪,只需要花几周时间就能适应了,而不需要去搞什么 SOAP, WSDL 等。

我认为通过 REST 定义你的业务接口,会显得更加简洁,业务逻辑也更加简单。

下面是一个 RESTful 的路由例子:

为了得到最好的 RESTful 设计,我经常把需要 REST 的资源和方法(例如: POST /balance/funds)写在临时的笔记本上,然后我把它们翻译成 RESTful 的路由。

总结

当你的代码有太多的逻辑,并且有太多的 concerns 的时候,你可以考虑分割你的 Controller。

它并不是意味着永远不要抽象,有些时候,同样的逻辑可能需要在好多 controller 之间共享,有时候一个分割的控制器只有一个很大的公开方法,这时候concerns, model 的方法还有 service 可以发挥作用了。

你的应用增长越快,不管代码多干净,就需要花更多时间去理解。但是如果做了分割 controller 的话,将会是理解变得更加容易。

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台