Rails:Skinny Controller vs. Fat Model,或者我应该让我的控制器厌食吗?

Posted

技术标签:

【中文标题】Rails:Skinny Controller vs. Fat Model,或者我应该让我的控制器厌食吗?【英文标题】:Rails: Skinny Controller vs. Fat Model, or should I make my Controller Anorexic? 【发布时间】:2010-03-31 03:46:13 【问题描述】:

我知道以前已经回答过类似的问题 - 例如:

逻辑应该去哪里 在哪里执行某些任务等。

但我有一个更具体的问题 - 我应该在多大程度上遵循这个公理:让你的控制器保持苗条,让你的模型变胖!

这是一个例子:

例如,假设我有多个验证数据来源。一个很好的例子是 VIN 号码 - 我可以根据制造商数据源、DMV 的数据源以及我的本地数据库来验证它——看看我有什么记录在案。 所以我有一个模型叫做 Vin 和 vins_controller。在模型内部,我有 5 种方法:

check_against_local_db check_against_dmv check_against_car_maker_1 check_against_car_maker_2 等

在与 REST 保持一致的控制器中,实际显示 - 我有一个简单的 case 语句,它查看 params[:source],并基于指定的源 - 将调用特定的检查方法。

现在问题来了: 我应该保留控制在控制器中调用哪个数据源的逻辑,还是应该将其移动到模型中,然后在控制器中执行 check_vin(source, vin) 之类的操作?

我应该让我的控制器厌食吗?

编辑

我正在将此转换为来自@jay-godse 的官方答案(谢谢 - 当时这是一个很好的答案)。 自 2010 年以来,情况发生了很大变化,这个问题仍然得到了一些看法——所以希望这会给一些人指明正确的方向,并帮助他们正确地组织代码。

Trailblazer gem 很好地解决了问题中提出的问题。

【问题讨论】:

【参考方案1】:

有句老话,

智能数据结构和愚蠢的代码 比其他方式好得多 周围。

您所描述的一切都是关于一条数据与另一条数据之间的关系。这本身就是这些关系的逻辑(包括验证)应该在模型或数据库层中的线索。

厌食症控制器是精心设计(智能?)数据的标志。我的经验告诉我,你在设计数据上投入的精力越多,你编写的代码就越少。

控制器最擅长解析输入,调用适当的模型,然后格式化输出。

【讨论】:

非常有趣。我很高兴读到这篇文章……并且还查到了这句话的出处:The Cathedral and the Bazaar +1 表示“控制器最擅长解析输入、调用适当的模型,然后格式化输出。【参考方案2】:

我会将逻辑放入我的模型中,尤其是在我进行 TDD 时(而且我总是 TDD,除非我不这样做。)测试模型通常比测试控制器容易得多。

【讨论】:

这也意味着您不需要在每个控制器中进行验证。【参考方案3】:

我喜欢通过思考责任来解决此类问题。在这种情况下,验证 VIN 的“责任”是什么?该模型。控制器只是在那里传递参数......根据用户输入“控制”。

如果不完全清楚,请这样想:如果需要重复使用这段代码,放在哪里会造成最小的影响?说...如果两个不同控制器中的两个不同操作都需要验证 VIN,需要做什么?如果你把这段代码留在控制器中,你基本上也必须在新控制器中复制它,但如果你把它放在模型中,你只需从新控制器调用 check_vin 方法,并且没有代码需要重复。通过在最有意义的地方分配职责,您提高了代码的可重用性。

【讨论】:

我想说“控制器代码用于 HTTP 客户端”...如果您(假设)在桌面应用程序中重用模型怎么办?这通常会理清代码应该去哪里。【参考方案4】:

你应该试试Trailblazer。这是一个在 Rails 之上的瘦框架(实际上,它与所有 Ruby 框架一起运行),它引入了服务层、表单对象、持久性和域代码之间的间接性等等。

它确实有助于保持所有软件组件的精简,因为它为您提供了更多的抽象层,让您更容易弄清楚在哪里放置什么。

例如,当您有一个表单对象时,为什么要将您的验证逻辑压入控制器和模型中?

【讨论】:

我将其切换为官方答案。自 2010 年以来,情况发生了很大变化,这个问题仍然有一些观点 - 所以希望这会给一些人指明正确的方向。 Trailblazer 很好地解决了问题中提出的问题。【参考方案5】:

“解析”params 是控制器的责任,但应在模型中进行验证。

我会在控制器上做这样的事情:

@my_model = MyModel.new(:source => params[:source] ...)
if(@my_model.valid?)
  # treat valid model here (i.e. save the model and redirect to show)
else
  # treat validation error here (usually throw an error)
end

您的控制器实际上只是“瘦”。对于真正的“厌食症”控制器,您可能想看看inherited_resources 或resource_this。在某些情况下,这些将为您提供一个 3 行控制器,实现整个 RESTful 堆栈。

【讨论】:

以上是关于Rails:Skinny Controller vs. Fat Model,或者我应该让我的控制器厌食吗?的主要内容,如果未能解决你的问题,请参考以下文章

ruby Rails Controller代码示例

Rails current_page?与 controller.controller_name

在 rails create controller (Rails 6) 中渲染 new.js.erb

Ruby on Rails如何自定义controller对应的view目录?

Rails:从URL解析路由信息

rails generate controller注释不创建它应该创建的文件