Rails中的OO设计:放置东西的位置

时间:2009-07-01 11:18:10

标签: ruby-on-rails ruby directory-structure

我真的很喜欢Rails(尽管我通常都是RESTless),而且我非常喜欢Ruby。仍然,制作巨大的ActiveRecord子类和巨大的控制器的趋势是很自然的(即使你每个资源都使用一个控制器)。如果你要创建更深层次的对象世界,你会在哪里放置类(和模块,我想)?我问的是(帮助者本身?),控制器和模型的观点。

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

4 个答案:

答案 0 :(得分:377)

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

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

Skinny控制器实际上是一个好主意,但推论 - 将所有内容放在模型中,并不是最好的计划。

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

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

此解决方案在某些情况下可以正常运行 - 在其他情况下,您将需要考虑在代码中使用模型,视图或控制器的类。

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

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

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

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

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

最重要的是,一旦你在Rails中找到了自己提出这个问题的观点,就应该加强你的Ruby chops并开始建模不仅仅是Rails为你提供的MVC类的类。默认。

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

答案 1 :(得分:62)

更新:问题的使用是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

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

帮助程序文件通常包含帮助程序方法,有时也包含辅助程序(例如Form Builders)使用对象时的类。

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

答案 2 :(得分:10)

  

......制造巨大的倾向   ActiveRecord子类和巨大的   控制器非常自然......

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

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

你的模型如何变得庞大?你是否应该考虑减少每堂课的责任?您是否可以将任何常见行为提取到mixins中?或者您可以委托给辅助类的功能区域?

编辑:尝试扩展一点,希望不要歪曲任何太糟糕的事情......

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

过滤器:假设您在多个操作中使用相同的代码行(通常使用params[:id]或类似的方法检索对象)。可以首先将复制抽象为单独的方法,然后通过在类定义中声明过滤器(例如before_filter :get_object)完全抽象出来。参见ActionController Rails Guide中的第6节。让声明性编程成为你的朋友。

重构模型更具宗教色彩。例如,Uncle Bob的门徒会建议您遵循SOLID的五诫。乔尔和杰夫may recommend是一种更加呃“务实”的方法,尽管它们后来似乎确实是little more reconciled。在类中查找一个或多个操作明确定义的属性子集的方法是尝试识别可能从ActiveRecord派生模型中重构的类的一种方法。

顺便说一句,Rails模型不必是ActiveRecord :: Base的子类。或者换句话说,模型不必是表格的模拟,甚至与存储的任何东西相关。更好的是,只要根据Rails的约定在app/models中命名文件(在类名上调用#underscore以找出Rails将要查找的内容),Rails就会发现它没有任何require是必要的。

答案 3 :(得分:1)

这是一篇很棒的博客文章,关于重构那些似乎来自“瘦身控制器”的胖子模特&#34;哲学:

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

基本信息是&#34;不要从Fat Models&#34;中提取Mixins,而是使用服务类,作者提供了7种模式