长期无安全方法链

所以我知道一些我所知道的不同方法,我想探索各种方法的优缺点,这些方法有以下几种:

  • 可读性
  • 性能
  • 易于调试
  • 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安全代码,而是在调用代码之前validation数据

 #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) 

我个人对猴子修补的看法一般是不要做,除非没有别的办法(甚至比我三思而后,如果我真的想要修补补丁)。
除了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 

你也可以阅读德米特的法律, 不要违反得墨忒耳的法则! 和模块#委托 。
至少我不会坚持使用Object#try只要一个简单的条件解决它,因为查看’try’的来源它比条件更昂贵。

我会避免使用长链,因为它们明显违反了得墨忒耳法 :

 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更容易,并且您的代码更容易阅读。

Demeter法则的问题在于你的class级对彼此了解太多,使他们更加紧密耦合,这反过来又使他们更加错误,更难以测试。

根据我的经验,最常见的违反“得墨忒耳法”的行为是在意见中。 既然你试图将名称大写,我猜这就是这种情况。 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) %> 

您可以通过向upcased_pet_name注入不同的值来测试upcased_pet_name。

现在:

  • 您的视图只知道可以访问“人员”,并且可以访问名为upcased_name的视图upcased_name
  • 您的Person模型只知道它有Pet方法。
  • 您的viewhelper只知道它可以接收名称方法或nil的实体。

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