Ruby-on-rails Rails 中的 OO 设计:把东西放在哪里

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1068558/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 21:25:17  来源:igfitidea点击:

OO Design in Rails: Where to put stuff

ruby-on-railsrubydirectory-structure

提问by Dan Rosenstark

I'm really enjoying Rails (even though I'm generally RESTless), and I enjoy Ruby being very OO. Still, the tendency to make huge ActiveRecord subclasses and huge controllers is quite natural (even if you do use a controller per resource). If you were to create deeper object worlds, where would you put the classes (and modules, I suppose)? I'm asking about views (in the Helpers themselves?), controllers and models.

我真的很喜欢 Rails(尽管我通常是 RESTless),而且我喜欢 Ruby 非常面向对象。尽管如此,制作巨大的 ActiveRecord 子类和巨大的控制器的趋势是很自然的(即使您确实为每个资源使用了一个控制器)。如果您要创建更深层次的对象世界,您会将类(和模块,我想)放在哪里?我问的是视图(在助手本身中?)、控制器和模型。

Lib is okay, and I've found some solutions to get it to reload in a dev environment, but I'd like to know if there's a better way to do this stuff. I'm really just concerned about classes growing too large. Also, what about Engines and how do they fit in?

Lib 没问题,我找到了一些解决方案来让它在开发环境中重新加载,但我想知道是否有更好的方法来做这些事情。我真的只是担心类变得太大。另外,引擎怎么样?它们是如何适应的?

回答by Yehuda Katz

Because Rails provides structure in terms of MVC, it's natural to end up using onlythe model, view, and controller containers that are provided for you. The typical idiom for beginners (and even some intermediate programmers) is to cram all logic in the app into the model (database class), controller, or view.

因为 Rails 根据 MVC 提供结构,所以最终使用为您提供的模型、视图和控制器容器是很自然的。初学者(甚至一些中级程序员)的典型习语是将应用程序中的所有逻辑塞入模型(数据库类)、控制器或视图中。

At some point, someone points out the "fat-model, skinny-controller" paradigm, and intermediate developers hastily excise everything from their controllers and throw it into the model, which starts to become a new trash can for application logic.

在某个时候,有人指出了“胖模型,瘦控制器”范式,中级开发人员仓促地从他们的控制器中剔除所有内容并将其扔到模型中,模型开始成为应用程序逻辑的新垃圾桶。

Skinny controllers are, in fact, a good idea, but the corollary--putting everything in the model, isn't really the best plan.

事实上,瘦控制器是一个好主意,但推论——将所有东西都放在模型中,并不是真正的最佳计划。

In Ruby, you have a couple of good options for making things more modular. A fairly popular answer is to just use modules (usually stashed in lib) that hold groups of methods, and then include the modules into the appropriate classes. This helps in cases where you have categories of functionality that you wish to reuse in multiple classes, but where the functionality is still notionally attached to the classes.

在 Ruby 中,您有几个不错的选择来使事情更加模块化。一个相当流行的答案是只使用lib包含方法组的模块(通常隐藏在 中),然后将模块包含到适当的类中。如果您有希望在多个类中重用的功能类别,但功能仍然在理论上附加到类的情况下,这会有所帮助。

Remember, when you include a module into a class, the methods become instance methods of the class, so you still end up with a class containing a tonof methods, they're just organized nicely into multiple files.

请记住,当您将一个模块包含到一个类中时,这些方法将成为该类的实例方法,因此您最终仍会得到一个包含大量方法的类,它们只是被很好地组织到多个文件中。

This solution can work well in some cases--in other cases, you're going to want to think about using classes in your code that are notmodels, views or controllers.

这个解决方案在某些情况下可以很好地工作——在其他情况下,你会想要考虑在你的代码中使用不是模型、视图或控制器的类。

A good way to think about it is the "single responsibility principle," which says that a class should be responsible for a single (or small number) of things. Your models are responsible for persisting data from your application to the database. Your controllers are responsible for receiving a request and returning a viable response.

一个很好的思考方式是“单一职责原则”,它说一个类应该负责单个(或少量)的事情。您的模型负责将数据从应用程序持久化到数据库。您的控制器负责接收请求并返回可行的响应。

If you have concepts that don't fit neatly into those boxes (persistence, request/response management), you probably want to think about how you wouldmodel the idea in question. You can store non-model classes in app/classes, or anywhere else, and add that directory to your load path by doing:

如果您有不完全满足需要的那些箱子(持久性,请求/响应管理)的概念,你可能要考虑如何有问题的想法建模。您可以将非模型类存储在 app/classes 或其他任何地方,并通过执行以下操作将该目录添加到您的加载路径:

config.load_paths << File.join(Rails.root, "app", "classes")

If you're using passenger or JRuby, you probably also want to add your path to the eager load paths:

如果您使用的是乘客或 JRuby,您可能还想将您的路径添加到急切加载路径:

config.eager_load_paths << File.join(Rails.root, "app", "classes")

The bottom-line is that once you get to a point in Rails where you find yourself asking this question, it's time to beef up your Ruby chops and start modeling classes that aren't just the MVC classes that Rails gives you by default.

最重要的是,一旦您在 Rails 中发现自己提出了这个问题,就该加强您的 Ruby 印章并开始对类进行建模,而不仅仅是 Rails 默认为您提供的 MVC 类。

Update:This answer applies to Rails 2.x and higher.

更新:此答案适用于 Rails 2.x 及更高版本。

回答by Simone Carletti

Update: The use of Concerns have been confirmed as the new default in Rails 4.

更新: 关注的使用已被确认为 Rails 4 中的新默认值

It really depends on the nature of the module itself. I usually place controller/model extensions in a /concerns folder within app.

这实际上取决于模块本身的性质。我通常将控制器/模型扩展放在 app 内的 /concerns 文件夹中。

# concerns/authentication.rb
module Authentication
  ...
end    

# controllers/application_controller.rb
class ApplicationController
  include Authentication
end



# concerns/configurable.rb
module Configurable
  ...
end    

class Model 
  include Indexable
end 

# controllers/foo_controller.rb
class FooController < ApplicationController
  include Indexable
end

# controllers/bar_controller.rb
class BarController < ApplicationController
  include Indexable
end

/lib is my preferred choice for general purpose libraries. I always have a project namespace in lib where I put all application-specific libraries.

/lib 是我首选的通用库。我总是在 lib 中有一个项目命名空间,我将所有特定于应用程序的库放在其中。

/lib/myapp.rb
module MyApp
  VERSION = ...
end

/lib/myapp/CacheKey.rb
/lib/myapp/somecustomlib.rb

Ruby/Rails core extensions usually take place in config initializers so that libraries are only loaded once on Rails boostrap.

Ruby/Rails 核心扩展通常发生在配置初始值设定项中,因此库仅在 Rails boostrap 上加载一次。

/config/initializer/config.rb
/config/initializer/core_ext/string.rb
/config/initializer/core_ext/array.rb

For reusable code fragments, I often create (micro)plugins so that I can reuse them in other projects.

对于可重用的代码片段,我经常创建(微)插件,以便我可以在其他项目中重用它们。

Helper files usually holds helper methods and sometimes classes when the object is intended to be used by helpers (for instance Form Builders).

Helper 文件通常包含 helper 方法,有时当对象打算由助手使用时(例如表单构建器)包含类。

This is a really general overview. Please provide more details about specific examples if you want to get more customized suggestions. :)

这是一个非常一般的概述。如果您想获得更多定制化建议,请提供有关具体示例的更多详细信息。:)

回答by Mike Woodhouse

... the tendency to make huge ActiveRecord subclasses and huge controllers is quite natural ...

...制作巨大的 ActiveRecord 子类和巨大的控制器的趋势是很自然的......

"huge" is a worrisome word... ;-)

“巨大”是一个令人担忧的词...... ;-)

How are your controllers becoming huge? That's something you should look at: ideally, controllers should be thin. Picking a rule-of-thumb out of thin air, I'd suggest that if you regularly have more than, say, 5 or 6 lines of code per controller method (action), then your controllers are probably too fat. Is there duplication that could move into a helper function or a filter? Is there business logic that could be pushed down into the models?

你的控制器是如何变得庞大的?这是您应该考虑的事情:理想情况下,控制器应该很薄。凭经验选择一个经验法则,我建议如果你经常有超过 5 或 6 行代码每个控制器方法(动作),那么你的控制器可能太胖了。是否存在可以移动到辅助函数或过滤器中的重复项?是否有可以下推到模型中的业务逻辑?

How do your models get to be huge? Should you be looking at ways to reduce the number of responsibilities in each class? Are there any common behaviours you can extract into mixins? Or areas of functionality you can delegate to helper classes?

你的模型如何变得巨大?您是否应该寻找减少每个类中职责数量的方法?有没有可以提取到 mixin 中的常见行为?或者您可以委派给辅助类的功能领域?

EDIT: Trying to expand a bit, hopefully not distorting anything too badly...

编辑:试图扩大一点,希望不会太严重地扭曲任何东西......

Helpers: live in app/helpersand are mostly used to make views simpler. They're either controller-specific (also available to all views for that controller) or generally available (module ApplicationHelperin application_helper.rb).

Helpers:住​​在里面,app/helpers主要用来让视图更简单。它们要么是特定于控制器的(也可用于该控制器的所有视图)或普遍可用(module ApplicationHelper在 application_helper.rb 中)。

Filters: Say you have the same line of code in several actions (quite often, retrieval of an object using params[:id]or similar). That duplication can be abstracted first to a separate method and then out of the actions entirely by declaring a filter in the class definition, such as before_filter :get_object. See Section 6 in the ActionController Rails GuideLet declarative programming be your friend.

过滤器:假设您在多个操作中使用同一行代码(通常,使用params[:id]或类似的方法检索对象)。通过在类定义中声明过滤器(例如before_filter :get_object. 请参阅ActionController Rails 指南中的第 6 节让声明式编程成为您的朋友。

Refactoring models is a bit more of a religious thing. Disciples of Uncle Bobwill suggest, for example, that you follow the Five Commandments of SOLID. Joel & Jeff may recommenda more, er, "pragmatic" approach, although they did appear to be a little more reconciledsubsequently. Finding one or more methods within a class that operate on a clearly-defined subset of its attributes is one way to try identifying classes that might be refactored out of your ActiveRecord-derived model.

重构模型更像是一种宗教性的事情。例如,鲍勃叔叔的门徒会建议您遵守SOLID的五诫。乔尔和杰夫可能会推荐一种更,呃,“务实”的方法,尽管他们后来似乎更和解一些。在类中查找一个或多个对其属性的明确定义的子集进行操作的方法是尝试识别可能从 ActiveRecord 派生模型中重构的类的一种方法。

Rails models don't have to be subclasses of ActiveRecord::Base, by the way. Or to put it another way, a model doesn't have to be an analogue of a table, or even related to anything stored at all. Even better, as long as you name your file in app/modelsaccording to Rails' conventions (call #underscore on the class name to find out what Rails will look for), Rails will find it without any requires being necessary.

顺便说一下,R​​ails 模型不必是 ActiveRecord::Base 的子类。或者换句话说,模型不必是表的类似物,甚至不必与存储的任何内容相关。更好的是,只要您app/models根据 Rails 的约定命名文件(在类名上调用 #underscore 以找出 Rails 将查找的内容),Rails 会找到它,而无需任何requires。

回答by bbozo

Here's an excellent blog post about refactoring the fat models that seem to arise from the "thin controller" philosphy:

这是一篇关于重构似乎源于“瘦控制器”哲学的胖模型的优秀博客文章:

http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/

http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/

Basic message is "Don't Extract Mixins from Fat Models", use service classes instead, the author provides 7 patterns to do so

基本信息是“不要从胖模型中提取 Mixins”,而是使用服务类,作者提供了 7 种模式来这样做