The Law of Demeter belongs to the principle that states in a ruby method of an object should invoke only the methods of the following kinds of objects:
- itself
- its parameters
- any objects it creates/instantiates
- its direct component objects
The law restricts how deeply a method can reach into another object’s dependency graph, preventing any one method from becoming tightly coupled to another object’s structure.
Multiple Dots
The most obvious violation of the Law of Demeter is “multiple dots,” meaning a chain of methods being invoked on each others’ return values.
class User
def discounted_plan_price(discount_code)
coupon = Coupon.new(discount_code)
coupon.discount(account.plan.price)
end
end
The call to account.plan.price
above violates the Law of Demeter by invoking price
on the return value of plan
. The price
method is not a method on User
, its parameter discount_code
, its instantiated object coupon
or its direct component account
.
The quickest way to avoid violations of this nature is to delegate the method:
class User
def discounted_plan_price(discount_code)
account.discounted_plan_price(discount_code)
end
end
class Account
def discounted_plan_price(discount_code)
coupon= Coupon.new(discount_code)
coupon.discount(plan.price)
end
end
In a Rails application, you can quickly delegate methods using ActiveSupport’s delegate
class method:
class User
delegate :discount_plan_price, to: :account
end
If you find yourself writing lots of delegators, consider changing the consumer class to take a different object