所以我知道一些我所知道的不同方法,并且我想探索各种方法的各种方法的优点和缺点:
明确使用来自有效支持的try方法
person.try(:pet).try(:name).try(:upcase)
使用救援无
person.pet.name.upcase rescue nil
使用&&运营商链
person && person.pet && person.pet.name && person.pet.name.upcase
猴子修补Object类,请参阅https://gist.github.com/thegrubbsian/3499234了解原始要点
class Object
def try_all(*methods)
values = [self]
methods.each do |method|
value = values.last.try(method)
return nil if value.nil?
values << value
end
values.last
end
end
person.try_all(:pet, :name, :upcase)
不要使用nil安全代码,而是在调用代码之前验证数据
#not a good implementation by any means
def person_has_pet_with_name? person
begin
return true if !person.pet.name.nil?
rescue
return false
end
end
person.pet.name.upcase if person_has_pet_with_name?(person)
答案 0 :(得分:4)
我个人对猴子修补的看法一般是不要做,除非没有别的办法(如果我真的想要修补补丁,我甚至会三思而后行。) 除了Rails已经膨胀了很多物体。因此,我不建议更多定制腹胀物体 为什么不通过经典的委托方法来避免违反德米特的法则:
class Person
attr_accessor :pet
delegate :name_upcased, to: :pet,
prefix: true, allow_nil: true
end
class Pet
attr_accessor :name
def name_upcased
@name.upcase if @name
end
end
@person.try :pet_name_upcased
你也可以在Do not break the law of Demeter!和Module#delegate阅读有关得墨忒耳的定律。
至少只要一个简单的条件解决它,我就不会坚持Object#try,因为看看&#39;尝试&#39;它比条件更昂贵。
答案 1 :(得分:0)
我会避免使用长链接,因为它们明显违反了Law of Demeter:
person.try(:pet).try(:name).try(:upcase)
如果您想要测试这样的代码以及某种方式存根,那么您的测试将变得非常复杂。我建议像下面这样的解决方案,其中这个链的复杂性在所涉及的类之间划分。这是面向对象的方式。在这里,没有一个类对另一个有太多了解。
class Person
def upcased_pet_name
pet.try(:upcased_name)
end
end
class Pet
def upcased_name
name.try(:upcase)
end
end
# Simple method call. nil checks are handled elsewhere
person.try(:upcased_pet_name)
在您的测试中,现在更容易存根person
,并且您的代码更容易阅读。
答案 2 :(得分:0)
Demeter法的问题在于你的班级对彼此了解太多,使他们更加紧密耦合,这反过来又使他们更加错误,更难以测试。
根据我的经验,最常见的违反“得墨忒耳法”的行为是在意见中。既然你试图将这个名字大写,我猜这就是这个例子。 SOOOO ....
您可以使用视图助手吗?对不起,Rails不太好,所以这是假的:
def upcased_name(entity_with_name)
if entity_with_name != nil
entity_with_name.name.try(:upcase)
end
end
然后在您看来,您只需致电
<% upcased_name(person.pet) %>
您可以通过向viewhelper注入不同的值来测试upcased_pet_name
。
现在:
upcased_name
的浏览器。Person
模型只知道它有Pet
方法。nil
接收实体。轰!你的课程只知道他们的朋友,而不知道朋友的朋友。