Rails:瘦控制器与胖模型,或者我应该让我的控制器厌食?

我知道之前已经回答了类似的问题 – 例如:

  • 逻辑应该去哪里
  • 在哪里做某些任务等

但是我有一个更具体的问题 – 我应该采取多少这个公理: 保持你的控制器瘦,让你的模特变胖!

这是一个例子:

例如,假设我有多个validation数据源。 一个很好的例子是VIN号码 – 我可以对它进行validation,制造商数据源,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方法。

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

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

编辑

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

开拓者gem解决了问题中提出的问题。

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

它确实有助于保持所有软件组件的紧密性,因为它为您提供了更多的抽象层,使得更容易找出放置内容的位置。

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

有句老话,

智能数据结构和哑代码比其他方式更好。

你所描述的一切都是关于一个数据如何与另一个数据相关。 这本身就是你的线索,这些关系的逻辑,包括validation应该在模型或数据库层。

厌食控制器是精心设计(智能?)数据的标志。 我的经验告诉我,您在设计数据方面付出的努力越多,整体编写的代码就越少。

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

我会把逻辑放在我的模型中,特别是如果我是TDD的(我总是TDD,除非我没有。)测试模型通常比测试控制器容易得多。

我喜欢通过思考责任来处理这样的问题。 在这种情况下,“负责”validationVIN是什么? 该模型。 控制器只是根据用户输入将参数传递给“控制”。

如果还不完全清楚的话,可以这样考虑一下:如果需要重新使用此代码会导致影响最小的地方? 说…如果两个不同控制器中的两个不同动作都需要validationVIN,那么需要做什么? 如果你将这段代码留在控制器中,你基本上也必须在新控制器中复制它,但如果你把它放在模型中,你只需从新控制器调用check_vin方法,而不是代码需要重复。 通过在最有意义的地方分配职责,您已经提高了代码的可重用性。

控制器负责“解析” params ,但validation应该在模型中完成。

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

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

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