【问题标题】:Fat model, thin controller - but what if mixed models?胖模型,瘦控制器 - 但是如果混合模型呢?
【发布时间】:2014-05-01 10:33:46
【问题描述】:

我正在构建一个 Ruby on Rails 4.1 应用程序,它具有以下模型,当您看到模型名称时,这些模型应该是有意义的:

域、团队、用户、会议等

现在,团队属于域,用户属于团队,会议也属于用户。域只是使用该软件的组织或公司。

创建管理员用户后,该用户必须首先创建域,然后创建第一个团队,然后其他用户才能注册。

正如您可能预期的那样,在创建初始域和团队的过程中(在团队和域控制器的新/创建中完成)我必须访问和更改所有三个中的引用。因此,例如,创建第一个域将必须关联管理员用户,创建团队将涉及将其链接到用户,然后是父域。

所以,这一切有点混乱,控制器需要访问多个模型。

我的问题是,这个逻辑属于哪里?本着瘦控制器的精神,您通常会将其外包给模型,但它涉及的模型不止一个。这是新的 Rails 4 关注点变得有用的地方还是我应该把它放在控制器中?

我对 Rails 比较陌生,所以如果您愿意回复,请记住这一点 - 谢谢!

【问题讨论】:

    标签: ruby-on-rails oop


    【解决方案1】:

    嵌套资源

    class Domain
      has_many :teams
    end
    
    class Team
      has_many :users
      belongs_to :domain
    end
    
    class User
      has_many :meetings
      belongs_to :team
    end
    
    class Meeting
      belongs_to :user
    end
    

    然后在你的控制器中:

    class TeamsController < ApplicationController
    
      def new
        @team = Team.new
      end
    
      def create
        @domain = Domain.find(params[:domain_id])
        @team   = @domain.teams.build(params[:team])
        @team.save
    
        respond_with @team
      end
    
    end
    

    这就是我们所说的“嵌套”控制器,在 routes.rb 文件中:

    resources :domain do
      resources :team
    end
    

    网址将如下所示:

    /domains/:domain_id/teams
    /domains/:domain_id/teams/:team_id
    

    同样的逻辑适用于其他模型。这应该为您提供构建应用程序的起点。

    下面一行

    @domain.teams.build(params[:team])
    

    自动将域链接到为您设置参考 (id) 的团队。

    但是,您不应该根据rails guide 进行深度嵌套,因此构建器设计模式可以派上用场。

    建造者设计模式

    但是,如果事情开始变得混乱,我建议使用专用的 ruby​​ 类来“构建”您的对象及其关系。我们通常将这些类称为“Builder”:

    class TeamBuilder
    
      attr_reader :domain, :params
    
      def initialize(domain, params = {})
        @domain = domain
        @params = params
      end
    
      def build
        domain.teams.build(params)
      end
    
    end
    

    它又在做非常简单的任务。例如,对于用户和会议:

    class UserBuilder
    
      attr_reader :team, :params
    
      def initialize(team, params = {})
        @team = team
        @params = params
      end
    
      def build
        team.users.build(params).tap do |user|
          user.foo = 'foo'
          user.meetings.build(...)
          user.meetings << MeetingBuilder.new(user, { ... })
        end
      end
    
    end
    
    class MeetingBuilder
      # ...
    end
    

    这里我们使用UserBuilder 中的MeetingBuilder 来建立会议。

    用法:

    user = UserBuilder.new(team, { ... }).build
    user.save
    

    【讨论】:

    • 这是我考虑过的——是时候再考虑一下了。我要花几个小时来评估这个 - 谢谢皮埃尔。
    • 你会嵌套多远呢?用户似乎也适合这种嵌套,但我现在记得我的问题是什么。我还有大约 5 个其他类(建筑、房间、出勤、预订、检查),它们也是相互关联的,当我查看嵌套时,我认为我最终可能会得到太多嵌套资源。但是,我看到你停在了最深处。你能说这是一个深思熟虑的选择吗?谢谢!
    • 是的,你应该避免深度嵌套,看看这里guides.rubyonrails.org/routing.html#nested-resources
    • 我认为构建器选项更好,因为路由过于复杂,考虑到相互交互的类的数量,我认为甚至仔细嵌套。感谢您的回答皮埃尔,我会接受的。
    • 你会将构建器类放在 Rails 4 目录结构中的什么位置?问题是拆分模型/控制器 - 所以,也许在帮助目录中?
    【解决方案2】:

    理想情况下,模型不应该关心甚至不知道其他类。所以在模型和控制器之间,这种逻辑肯定是属于控制器的。

    不过,我可能会选择第三类,处理那些乱七八糟的东西,例如 DomainFactory。这只是一个普通的旧 ruby​​ 对象 (poro),没有活动记录或任何东西,其唯一目的是创建域。

    一个提示是阅读松散耦合单一职责

    【讨论】:

      猜你喜欢
      • 2012-01-25
      • 1970-01-01
      • 1970-01-01
      • 2015-04-24
      • 1970-01-01
      • 2010-10-02
      • 2013-03-12
      • 2012-02-02
      • 2012-12-12
      相关资源
      最近更新 更多