我有一些模型,其视图代码如下所示
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 %>
上面的视图代码并不理想。为了正确封装, 发票不应跨越客户对象到达街道属性 地址对象。因为,例如,将来您的应用程序将会发生变化 这样客户就可以在每个地方都有账单地址和送货地址 跨越这些对象来检索街道的代码会破坏 需要改变。我该如何避免这个问题?
答案 0 :(得分:2)
一个简单的解决方案是客户有一个方法将返回其主要地址,如:
class Customer < ActiveRecord::Base
def main_address
self.address
end
end
如果仅通过此方法访问其地址,则当它具有多个地址时,只需更改main_address
方法即可执行任何操作。
修改1:
另一种选择是使用@soundar
建议的delegateclass 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 %>