Ruby and loose coupling

Hi guys,

Am trainning on Ruby and I was wondering if this implementation would be a good approach to loose coupling best practices?

class User
  def share(thing)
    if thing.methods.include?(:share)
      thing.share
    else
      Rails.logger.info "You can't share this object."
    end
  end
end

Many tanks

First of all - Ruby has respond_to? method
Second of all - It’s rather a bad practice.

why? Because there is this OOP design rule that you should Tell don't ask! It means that you shouldn’t ask an object if you can do something with it you should just tell it to do it. The other thing is that you should fail fast and by fail fast I mean you shouldn’t try to hide the error because you will probably catch it sooner or later and the later you catch it the harder it is to figure out what is the reason. In this case you should fail if the thing does not support share method. If you pass an object without share method it probably means it shouldn’t be passed there in the first place and it is a bug you have to fix.

Thanks for your answer. It helps a lot :blush:!

The pattern that plays nice with this rule is NullObject pattern. If you don’t have the right object then pass the object that represents empty value and has corresponding public api. It can be for example

class NoThing
  def share
    Rails.logger.info "Tried to share nothing"
  end
end

thing = Something.find_by(params) || NoThing.new
User.share(thing)

But what if Something isn’t the only object type which can be shared? Like with a class called SomethingElse. What would you do since there isn’t any interface in Ruby?

Edit : I guess it’s fine since we don’t need the security provided by interfaces/abstract classes which prevent you from using an object without share method implemented…

Everything that has share method will be valid argument. Your interface is your public API only not explicitly defined. You can also create a class if you want to have it written down

class Thing
  def share
    fail NotImplementedError
  end
end

class Something < Thing
  def share
    ...
  end
end

or even better with a module

module ThingBehavior
  def share
    fail NotImplementedError
  end
end

class Something
  include ThingBehavior

  def share
    ...
  end
end

Good point, thank you :smile: