Long nil safe method chains [closed]

我的未来我决定 提交于 2019-12-21 05:05:01

问题


So I know of a few different approaches that I am aware of and I want explore the advantages and disadvantages of the various ways for various criteria which are:

  • readability
  • performance
  • ease of debugging
  • OO principles (low coupling and high cohesion)

Explicitly using the try method from active support

person.try(:pet).try(:name).try(:upcase)

Using a rescue nil

person.pet.name.upcase rescue nil

Using an && operator chain

person && person.pet && person.pet.name && person.pet.name.upcase

Monkey patching the Object class, see https://gist.github.com/thegrubbsian/3499234 for the original gist

 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)

Don't have nil safe code, instead validate the data before you call the code

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

回答1:


My personal opinion about monkey patching in general is do NOT do it unless there is no other way (and even than I think twice if I really want to monkey patch).
Besides Rails already bloated objects a lot. So I'd not suggest custom bloating objects even more.
Why not avoiding to break the law of Demeter by the classic delegator approach:

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

You can also read about the law of Demeter at Do not break the law of Demeter! and Module#delegate.
At least I would not stick to Object#try as long as a simple condition solves it, because looking at the source of 'try' it is more costly than the condition.




回答2:


I'd avoid the long call chains as they're a clear violation of the Law of Demeter:

person.try(:pet).try(:name).try(:upcase)

If you'd want to test code like that and somehow stub person, your tests will become extremely complex. I would suggest a solution like the following, where the complexity of this chain is divided between the classes involved. This is the Object-Oriented way. Here, none of the classes knows too much about the other.

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)

In your tests, it's now a lot easier to stub person, and your code is a lot easier to read.




回答3:


The problem with the Law of Demeter is that your classes know too much about each other, making them more tightly coupled, which in turn makes them more buggy and harder to test.

In my experience, the most frequent violations of the Law of Demeter are in views. Since you are trying to capitalise the name, I'm guessing that's the case here. Soooo....

Can you use a view helper? Sorry, not much good at Rails, so this is pseudocodish:

def upcased_name(entity_with_name)
    if entity_with_name != nil
      entity_with_name.name.try(:upcase)
    end
end

Then in your view, you just call

<% upcased_name(person.pet) %>

You can test the upcased_pet_name by injecting different values to the viewhelper.

Now:

  • Your view knows only that is has access to a 'Person', and access to a viewhelper called upcased_name.
  • Your Person model knows only that it has a Pet method.
  • Your viewhelper knows only that it can receive an entity with a name method, or a nil.

Boom! Your classes know only about their friends, and nothing about the friends of their friends.



来源:https://stackoverflow.com/questions/25758830/long-nil-safe-method-chains

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!