ChatGPT解决这个技术问题 Extra ChatGPT

Rails 中的 OO 设计:把东西放在哪里

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

Lib 没问题,我找到了 some solutions to get it to reload in a dev environment,但我想知道是否有更好的方法来做这些事情。我真的只是担心类变得太大。另外,引擎怎么样?它们如何适应?


A
Adam Zerner

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

在某些时候,有人指出“胖模型,瘦控制器”范式,中级开发人员匆忙从控制器中删除所有内容并将其扔到模型中,这开始成为应用程序逻辑的新垃圾箱。

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

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

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

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

考虑它的一个好方法是“单一责任原则”,它表示一个类应该对单一(或少量)事物负责。您的模型负责将数据从应用程序保存到数据库。您的控制器负责接收请求并返回可行的响应。

如果您有不完全适合这些框的概念(持久性、请求/响应管理),您可能想考虑如何对相关想法进行建模。您可以将非模型类存储在 app/classes 或其他任何位置,然后通过执行以下操作将该目录添加到您的加载路径:

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

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

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

底线是,一旦你在 Rails 中发现自己提出这个问题,是时候加强你的 Ruby 技能并开始建模类,而不仅仅是 Rails 默认为你提供的 MVC 类。

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


哦。我没有想到为非模型添加一个单独的目录。我能感觉到正在收拾东西……
耶胡达,谢谢你。很好的答案。这正是我在我继承的应用程序(以及我制作的应用程序)中看到的:控制器、模型、视图以及自动为控制器和视图提供的助手中的所有内容。然后来自 lib 的 mixins,但从来没有尝试过真正的 OO 建模。不过,您是对的:在“应用程序/课程或其他任何地方”。只是想检查一下我是否缺少一些标准答案...
对于更新的版本,config.autoload_paths 默认为 app 下的所有目录。所以你不需要像上面描述的那样改变 config.load_paths 。不过,我不确定 eager_load_paths (还),需要调查一下。有人已经知道了吗?
对中间体的被动攻击:P
如果 Rails 附带这个“类”文件夹来鼓励“单一责任原则”并让开发人员能够创建不支持数据库的对象,那就太好了。 Rails 4 中的“关注”实现(参见 Simone 的回答)似乎已经处理了实现模块以在模型之间共享逻辑。但是,没有为不支持数据库的普通 Ruby 类创建这样的工具。鉴于 Rails 非常固执己见,我很好奇不包括这样的文件夹背后的思考过程?
S
Simone Carletti

更新:关注的使用已confirmed as the new default in Rails 4

这实际上取决于模块本身的性质。我通常将控制器/模型扩展放在 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 是我对通用库的首选。我总是在 lib 中有一个项目名称空间,我将所有特定于应用程序的库放在其中。

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

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

Ruby/Rails 核心扩展通常发生在配置初始化程序中,因此库只在 Rails boostrap 上加载一次。

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

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

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

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


奇怪的事情。我无法让这个 require_dependency RAILS_ROOT + "/lib/my_module" 与 lib 目录之外的东西一起工作。如果找不到文件,它肯定会执行并抱怨,但不会重新加载它。
Ruby 的 require 只加载一次。如果您想无条件加载某些内容,请使用 load。
此外,对我来说,您希望在应用程序实例的生命周期内两次加载文件似乎很不寻常。您是否正在生成代码?
为什么使用 require_dependency 而不是 require?另请注意,如果您遵循命名约定,则根本不需要使用 require。如果您在 lib/my_module 中创建 MyModule,则可以在没有先前的 require 的情况下调用 MyModule(即使使用 require 应该更快并且有时更具可读性)。另请注意,/lib 中的文件仅在引导时加载一次。
使用关注点是关于
C
Community

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

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

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

你的模型是如何变得庞大的?您是否应该寻找减少每个班级的责任数量的方法?您可以将任何常见行为提取到 mixins 中吗?或者您可以委托给辅助类的功能领域?

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

助手:住在 app/helpers 中,主要用于简化视图。它们要么是特定于控制器的(也可用于该控制器的所有视图),要么是普遍可用的(application_helper.rb 中的 module ApplicationHelper)。

过滤器:假设您在多个操作中具有相同的代码行(通常是使用 params[:id] 或类似方法检索对象)。这种重复可以首先抽象为一个单独的方法,然后通过在类定义中声明一个过滤器(例如 before_filter :get_object)完全脱离操作。请参阅 ActionController Rails Guide 让声明式编程成为您的朋友中的第 6 节。

重构模型更像是一种宗教性的事情。例如,Uncle Bob 的门徒会建议您遵守 SOLID 的五诫。乔尔& Jeff may recommend 是一种更、呃、“务实”的方法,尽管后来他们确实看起来是一个 little more reconciled。在一个类中找到一个或多个对其明确定义的属性子集进行操作的方法是尝试识别可能从 ActiveRecord 派生模型中重构出来的类的一种方法。

顺便说一下,Rails 模型不必是 ActiveRecord::Base 的子类。或者换一种说法,模型不必是表格的模拟物,甚至根本不需要与存储的任何东西相关。更好的是,只要您根据 Rails 的约定在 app/models 中命名您的文件(在类名上调用#underscore 以找出 Rails 将查找的内容),Rails 就可以找到它而无需任何 require


无论如何,迈克,感谢您的关注...我继承了一个项目,其中控制器上有一些巨大的方法。我已将它们分解为更小的方法,但控制器本身仍然“胖”。所以我正在寻找的是我卸载东西的所有选项。你的答案是“辅助函数”、“过滤器”、“模型”、“混合”和“辅助类”。那么,我可以把这些东西放在哪里呢?我可以组织一个在开发环境中自动加载的类层次结构吗?
b
bbozo

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

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

基本信息是“不要从 Fat Models 中提取 Mixins”,改用服务类,作者提供了 7 种模式来做到这一点