Rails:瘦控制器与胖模型,或者我应该让我的控制器厌食症?
我知道以前已经回答过类似的问题 - 例如:
- 逻辑应该在哪里
- 执行某些任务,等等。
但我有一个更具体的问题 - 我应该走多远这个公理:保持你的控制器瘦,让你的模型胖!
这是一个示例:
例如,假设我有多个验证数据源。一个很好的例子是 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 很好地解决了问题中提出的问题。
I know similar questions have been answered before - such as:
- Where should logic go
- where to do certain tasks, etc.
But I have a more specific question - How far should I take this axiom: keep your controller skinny, make your model fat!
Here is an example:
For instance let's say I have multiple source of verification data. A good example would be a VIN number -
I can verify it against, manufacturers data source, DMV's data source, also my local databases - to see what I have on record.
So I have a model called Vin and vins_controller. Inside the model I have 5 methods:
- check_against_local_db
- check_against_dmv
- check_against_car_maker_1
- check_against_car_maker_2, etc.
In my controller keeping with the REST, in action show - I have a simple case statement which looks at the params[:source], and based on source specified - will call specific check method.
Now here is the question:
Should I leave the logic that governs which data source to call in controller or should I move it to model and then in controller just do something like check_vin(source, vin)?
Should I make my controller anorexic?
EDIT
I'm switching this to official answer from @jay-godse ( thank you - at the time it was a good answer).
Things changed a lot since 2010 and this question still gets some views - so hopefully this will point some people in the right direction and help them organize their code properly.
Trailblazer gem addresses problems brought up in the question really well.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
有句老话说,
您所描述的所有内容都是关于一项数据如何与另一项数据相关的。这本身就是您的线索,即这些关系的逻辑(包括验证)应该位于模型或数据库层中。
厌食控制器是精心设计(智能?)数据的标志。我的经验告诉我,您在数据设计上投入的精力越多,总体上需要编写的代码就越少。
控制器最擅长解析输入,调用适当的模型,然后格式化输出。
There is an old saying,
Everything you have described is about how one piece of data relates to another. That itself is your clue that the logic for those relationships, including validation should be in the model or database layer.
An anorexic controller is a sign of well-designed (smart?) data. My experience tells me that the more effort you put into designing your data, the less code you have to write overall.
Controllers are best at parsing inputs, calling the appropriate models, and then formatting the outputs.
我会将逻辑放入我的模型中,尤其是当我进行 TDD 时(而且我总是 TDD,除非我不这样做。)测试模型通常比测试控制器容易得多。
I would put the logic in my model, especially if I'm TDD'ing (and I always TDD, except when I don't.) Testing the model is usually much easier than testing the controller.
我喜欢通过思考责任来解决这样的问题。在这种情况下,什么“负责”验证 VIN?模型。控制器只是传递参数......以根据用户输入进行“控制”。
如果还不完全清楚,请这样想:如果需要重用此代码,将其放在哪里会造成最小的影响?假设...如果两个不同控制器中的两个不同操作都需要验证 VIN,那么需要做什么?如果您将此代码留在控制器中,则实际上也必须在新控制器中复制它,但如果您已将其放置在模型中,则只需从新控制器中调用 check_vin 方法,而无需任何代码需要重复。通过将职责分配到最有意义的地方,您提高了代码的可重用性。
I like to approach questions like this by thinking about responsibility. What in this case is "responsible" for verifying the VIN? The model. The controller is simply there to pass along the parameters...to "control" based on user input.
If it's not entirely clear, think of it this way: where will putting this code cause the least amount of impact if it needs to be re-used? Say...if two different actions in two different controllers both need to verify a VIN, what would need to be done? If you left this code in the controller, you'd essentially have to duplicate it in the new controller as well, but if you had placed it in the model, you'd simply call the check_vin method from the new controller, and no code duplication would be needed. By assigning responsibilities where they make the most sense, you've improved the re-usability of your code.
您应该尝试一下 Trailblazer。这是一个基于 Rails 的瘦框架(实际上,它与所有 Ruby 框架一起运行),它引入了服务层、表单对象、持久性和域代码之间的间接性等等。
它确实有助于保持所有软件组件的精简,因为它为您提供了更多的抽象层,使您更容易弄清楚在哪里放置什么。
例如,当您有一个表单对象时,为什么要将验证逻辑压入控制器和模型中?
You should give Trailblazer a go. This is a thin framework on top of Rails (actually, it runs with all Ruby frameworks), it introduces a service layer, form objects, indirection between persistence and domain code, and so on.
It really helps to keep all your software components skinny as it gives you more abstraction layers that make it so much easier to figure out where to put what.
For example, why would you press your validation logic into controller and model when you have a form object for that?
控制器有责任“解析”
params
,但验证应该在模型中完成。我会在控制器上做这样的事情:
你的控制器实际上只是“瘦”。对于真正的“厌食症”控制器,您可能需要查看 inherited_resources 或 resource_this。在某些情况下,这些将为您提供一个 3 行控制器,实现整个 RESTful 堆栈。
It's the controller's responsibility to "parse"
params
, but validation should be done in the model.I would do something like this on the controller:
Your controller will actually be just "skinny". For truly "anorexic" controllers, you might want to have a look at inherited_resources or resource_this. In some cases these will give you a 3-lines controller, implementing the whole RESTful stack.