Chains of reliable methods with a long zero

Therefore, I know several different approaches that I know of, and I want to study the advantages and disadvantages of various methods for various criteria, which are:

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

Explicit use of the try method from active support

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

Using an emergency nickname

 person.pet.name.upcase rescue nil 

Using && Operator Chain

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

Monkey correcting 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) 

You don't have a nil security code, check the data before calling the code instead

 #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) 
+5
source share
3 answers

My personal opinion about the monkey patch does not do this at all if there is no other way (and even if I think twice, if I really want the monkey patch).
In addition, Rails has already inflated objects. Therefore, I no longer offer individual bloating.
Why not avoid breaking Demeter’s law with a classic approach to a delegate:

 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 in Do not break the law of Demeter! and module delegate # . At least I would not stick to Object # try until a simple condition solves it, because looking at the source of "try" is more expensive than the condition.

+4
source

I would avoid long chains of calls, as they are a clear violation of the law of the Law of Demeter :

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

If you want to test such code and somehow stifle a person, your tests will become extremely complex. I would suggest a solution similar to the following, where the complexity of this chain is shared between the participating classes. This is an object oriented way. Here, none of the classes know 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 is now much easier to drown out person , and your code is much easier to read.

0
source

The problem with the Demeter Law is that your classes know too much about each other, making them more closely connected, which in turn makes them more difficult and difficult to test.

In my experience, the most common violations of the Demeter Law are in the eyes. Since you are trying to make amends for the name, I assume the point is here. Soooo ...

Can a view helper be used? Sorry, not very good in Rails, so this is pseudocode:

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

Then, in your opinion, you just call

 <% upcased_name(person.pet) %> 

You can test upcased_pet_name by entering various values ​​in the viewhelper.

Now:

  • In your view, you only know that it has access to the Face and access to a viewer called upcased_name .
  • Your Person model only knows that it has a Pet method.
  • Your observer only knows that he can get an object with a name method or nil .

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

0
source

Source: https://habr.com/ru/post/1202215/


All Articles