Long nil safe method chains [closed]

与世无争的帅哥 提交于 2019-12-03 16:14:34

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.

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.

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.

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