Reputation: 4510
I am currently learning about delegate
and the Law of Demeter, and I can't seem to find a good example of where using delegate
would actually even be useful.
I want to find an example related to my project, since I have to give a presentation. The only line of code that I found that could possibly be breaking the Law of Demeter is the following:
@game.promotions.find_by_promo_type("cross")
The model Game
has_many
Promotions
and it is reaching across another model to execute a find
call based on promotions attribute promo_type
. From my understanding this is a violation of the Law of Demeter, and I should fix this by using delegate as so:
class Game < ActiveRecord::Base
has_many :promotions
delegate :find_by_promo_type, :to => :promotion
end
Can you give me an example where this would actually be useful besides "the Law of Demeter says so".
The only thing I can think of, is for some reason I wanted to change the name of promotions
to promos
, then the solution would be useful because I would only need to make the following change and :find_by_promo_type
will still work for Game
class Game < ActiveRecord::Base
has_many :promos
delegate :find_by_promo_type, :to => :promos
end
The only thing is, I believe this argument is flawed. If I were to change a models name, I would also have to refactor code in many other places that don't even break the Law of Demeter. It's hard to believe that this is all the Law of Demeter can accomplish in regards to this example.
Can somebody please help me understand this.
Upvotes: 4
Views: 1553
Reputation: 251
Using delegate
can have the advantage of abstracting the implementation of how something is done. In your example, it's not so much about allowing you to change the name of promotions
to promos
. It's about being very purposeful about what you expose to the "outside world", or, the API of your object. Objects that talk to Game
shouldn't need to know that you're using another ActiveRecord model to retrieve associated Promotions
. It can add unnecessary coupling.
All LoD violations aren't necessarily bad, but they can act as a code smell, and you should take pause when you see them. You need to balance the cost of removing the LoD violation against the potential cost and likelihood of change to your object's API.
I would highly recommend reading Chapter 4 of Practical Object-Oriented Design in Ruby: An Agile Primer by Sandi Metz. It covers this subject very well and states that: "Using delegation to hide tight coupling is not the same as decoupling the code."
In your case, you may want to think about it more from the perspective of the caller and what message it should be sending to Game
. Maybe it should just be calling Game.cross_promotions
instead of reaching into Game
to call a method on another object, regardless if it's delegated or not.
Upvotes: 6