长零安全方法链

时间:2014-09-10 06:37:16

标签: ruby-on-rails activesupport

所以我知道一些我所知道的不同方法,并且我想探索各种方法的各种方法的优点和缺点:

  • 可读性
  • 性能
  • 易于调试
  • OO原则(低耦合和高内聚)

明确使用来自有效支持的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)

3 个答案:

答案 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方法。
  • 您的viewhelper只知道它可以通过名称方法或nil接收实体。

轰!你的课程只知道他们的朋友,而不知道朋友的朋友。