如何避免像@ invoice.customer.address.street等方法调用链?

时间:2012-10-29 12:44:17

标签: ruby-on-rails ruby-on-rails-3 ruby-on-rails-3.2

我有一些模型,其视图代码如下所示

class Address < ActiveRecord::Base
  belongs_to :customer
end

class Customer < ActiveRecord::Base
  has_one :address
  has_many :invoices
end

class Invoice < ActiveRecord::Base
  belongs_to :customer
end

此代码显示简单的发票结构,客户具有单个地址。 显示发票地址行的视图代码如下:

<%= @invoice.customer.name %>
<%= @invoice.customer.address.street %>
<%= @invoice.customer.address.city %>,
<%= @invoice.customer.address.state %>
<%= @invoice.customer.address.zip_code %>

上面的视图代码并不理想。为了正确封装, 发票不应跨越客户对象到达街道属性 地址对象。因为,例如,将来您的应用程序将会发生变化 这样客户就可以在每个地方都有账单地址和送货地址 跨越这些对象来检索街道的代码会破坏 需要改变。我该如何避免这个问题?

3 个答案:

答案 0 :(得分:2)

一个简单的解决方案是客户有一个方法将返回其主要地址,如:

class Customer < ActiveRecord::Base
  def main_address
    self.address
  end
end

如果仅通过此方法访问其地址,则当它具有多个地址时,只需更改main_address方法即可执行任何操作。

修改1:

另一种选择是使用@soundar

建议的delegate
class Invoice < ActiveRecord::Base
  belongs_to :customer
  delegate :address, :to => :customer
end

答案 1 :(得分:0)

您可以使用委托功能来减少函数调用的顺序。

class Invoice < ActiveRecord::Base
  belongs_to :customer
  delegate :address, :to => :customer
end

<%= @invoice.customer.name %>
<%= @invoice.address.street %>
<%= @invoice.address.city %>,
<%= @invoice.address.state %>
<%= @invoice.address.zip_code %>

答案 2 :(得分:0)

为了避免上述问题,请务必遵循Law of Demeter,也称为Principle of Least Knowledge

要遵循Demeter法则,您可以按如下方式重写上述代码:

class Address < ActiveRecord::Base
  belongs_to :customer
end

class Customer < ActiveRecord::Base
  has_one :address
  has_many :invoices

  def street
    address.street
  end

  def city
    address.city
  end

  def state
    address.state
  end

end

class Invoice < ActiveRecord::Base
  belongs_to :customer

  def customer_name
    customer.name
  end

  def customer_street
    customer.street
  end

  def customer_state
    customer.state
  end
end

您可以将视图代码更改为以下内容:

<%= @invoice.customer_name %>
<%= @invoice.customer_street %>
<%= @invoice.customer_city %>
<%= @invoice.customer_state %>

在上面的代码中, Invoice 上的公共接口已被可能与您的发票界面的其余部分无关的方法污染。这是Demeter法的一般缺点,它并不特别针对Ruby on Rails。

现在,该方法是类级delegate方法。此方法提供了一个快捷方式,用于指示将在对象上创建的一个或多个方法实际上由相关对象提供。使用此委托方法,您可以像这样重写您的示例:

class Address < ActiveRecord::Base
  belongs_to :customer
end

class Customer < ActiveRecord::Base
  has_one :address
  has_many :invoices

  delegate :street, :city, :state, :to => :address
end

class Invoice < ActiveRecord::Base
  belongs_to :customer

  delegate :name, :street, :city, :state, :to => :customer, :prefix => true
end

在这种情况下,您无需更改视图代码,方法就像以前一样公开:

<%= @invoice.customer_name %>
<%= @invoice.customer_street %>
<%= @invoice.customer_city %>
<%= @invoice.customer_state %>