首页 文章

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

提问于
浏览
239

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

Lib没关系,我找到了some solutions to get it to reload in a dev environment,但我是一个更好的方法来做这些事情 . 我真的只是担心课程变得太大了 . 那么,Engines怎么样?它们如何适应?

4 回答

  • 62

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

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

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

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

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

    在某些情况下,此解决方案可以很好地工作 - 在其他情况下,您将需要考虑在代码中使用 not 模型,视图或控制器的类 .

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

    如果您的概念不能完全适合这些框(持久性,请求/响应管理),您可能想要考虑如何对所讨论的想法进行建模 . 您可以在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类 .

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

  • 376

    Update :关注的使用已经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) .

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

  • 1

    ...制作巨大的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更多,呃,"pragmatic"方法,虽然他们后来确实似乎是little more reconciled . 在类中查找一个或多个操作明确定义的属性子集的方法是尝试识别可能从ActiveRecord派生模型中重构的类的一种方法 .

    Rails模型必须是表格的模拟,甚至与存储的任何内容相关 . 更好的是,只要你根据Rails的惯例在 app/models 中命名你的文件(在类名上调用#underscore以找出Rails将要查找的内容),Rails就会发现它没有任何 require 是必要的 .

  • 10

    这是一篇很棒的博客文章,关于重构那些似乎来自“瘦控制器”philosphy的胖模型:

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

    基本消息是“不要从Fat模型中提取Mixins”,而是使用服务类,作者提供了7种模式

相关问题