1

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

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 %>

上面的视图代码并不理想。为了正确封装,发票不应跨越客户对象到达地址对象的街道属性。因为,例如,如果将来您的应用程序要更改,以便客户同时拥有帐单地址和送货地址,那么您的代码中跨越这些对象以检索街道的每个地方都会中断并且需要更改。我怎样才能避免这个问题?

4

3 回答 3

2

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

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

如果您仅通过此方法访问其地址,则当它具有多个地址时,只需更改main_address方法以执行您想做的任何事情。

编辑1:

另一种选择是使用@soundar 建议的委托

class Invoice < ActiveRecord::Base
  belongs_to :customer
  delegate :address, :to => :customer
end
于 2012-10-29T12:48:19.520 回答
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 %>
于 2012-10-29T13:19:29.417 回答
0

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

要遵循得墨忒耳法则,您可以将上面的代码重写如下:

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上的公共接口已经被可以说与发票接口的其余部分无关的方法污染了。这是得墨忒耳法则的一个普遍缺点,它并不是特别针对 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 %>
于 2012-10-30T06:32:15.887 回答