委派或实例化其他课程?

时间:2013-02-08 00:48:57

标签: ruby-on-rails-3 refactoring delegation

假设我有一个Account类和一个AccountReport类。在帐户#show中,我想显示一个帐户的报告。 Account和AccountReport都有许多公共方法。以下哪种技术更好?

1)实例化帐户和AccountReport,使用帐户数据初始化AccountReport。

class AccountsController < ActionController::Base
  def show
    @account = current_user.account
    @account_report = AccountReport.new(@account.orders)

    respond_with(@account)
  end

  # ...
end

2)允许Account实例实例化AccountReport并委托方法调用

class Account < ActiveRecord::Base
  attr_reader :account_report

  delegate :method_a, :method_b, :method_c, :method_d, :to => :account_report

  after_initialize :setup_account_report

  def setup_account_report
    @account_report = AccountReport.new(orders)
  end

  # ...
end

选项2对我来说似乎是一种更清洁的方法,但是用很多方法加载帐户会使它感觉像上帝一样。

2 个答案:

答案 0 :(得分:2)

好吧,我认为你必须混合两种选择。

如果你只使用show上的报告,第一个是好的。 如果您为帐户使用所有时间报告,则第二个是好的。

使用第二个,您的报告将一直实例化,这可能会降低性能。

你应该尝试这样的事情:

class Account < ActiveRecord::Base
  # ...

  @report = nil
  def report
    if @report.nil?
       @report = AccountReport.new(self.orders)
    end
  end

  # ...
end

此解决方案的好处是报表仅在需要时加载,但不会每次都加载。 这个解决方案的坏处是,如果你添加一些订单,你的报告就不会是最新的。

<强>更新 要改善这一点,您可以用这个替换条件

if @report.nil || self.created_at_changed?

答案 1 :(得分:0)

我喜欢第一个选项,因为它保持低耦合。第二个选项以可能不必要的方式将Account和AccountReport绑定在一起。每当您收到其他类型的报告时会发生什么?您可能需要更改Account中的一堆内容,这很难过,因为它们看似无关。

您可以通过在服务对象中组合这两个内容并将其交给您的视图来控制控制器中的逻辑/详细程度。 AccountReporting服务可以处理将这两个类组合在一起的逻辑,例如:

class AccountReporting
    def initialize(account)
       @account = account
    end
    def report
       AccountReport.new(account.orders)
    end
end

然后,在控制器中使用它:

AccountReporting.new(current_user.account)

这有意义吗?