The Law of Demeter (LoD) is a design guideline mainly used in object-oriented software development.
This presentation was given at a Stacked! Meetup by Michael Elfassy of Smashing Boxes. Learn more about Stacked! at meetup.com/stacked or stackedconf.org.
3. Definition
1. Each unit should have only limited knowledge about
other units
2. Each unit should only talk to its friends; don't talk to
strangers.
3. Only talk to your immediate friends.
source: Wikipedia
4. In other words
An object should only invoke methods of these kind of
objects:
1. itself
2. its parameters
3. any objects it creates
4. its direct component objects
5. objects of the same type*
11. Violations
try().try()
smells bad!
class User
belongs_to :department
def division_name
# we know the user has a department
dep = department
# we're assuming the department has a function called division
# which returns the associated division object
# we're assuming that every department belongs to a division
div = dep.division
# we're assuming the division has a name attribute
division_name = div.name
end
end
12. Dependency assumption
● Duplication
o Once you traverse in one place you are likely to traverse in another place
● Few places understand the relationships
● Don’t create “context”
● at each step of the traversal, the value
returned is decided by the object being
asked
13. Following the Law
class User
belongs_to :department
def division_name
# ask the department for a division name (you decide how you are going to get it!)
department.try(:division_name)
end
end
class Department
belongs_to :division
def division_name
# ask the division for a name (you decide how you are going to get it!)
division.name
end
end
14. The rails way(™)
class User
belongs_to :department
delegate :division_name, to: :department, allow_nil: true
end
class Department
belongs_to :division
delegate :name, to: :division, prefix: true
delegate :director, to: :division
end
15. Testing
Now we can test models independently without
having to do nested stubs!
16. I am the law
let’s replace all . with _ !
NO…
Don’t only fix the violations, look at what they
are telling you. Refactor!
17. Wallet example
class Wallet
attr_accessor :cash
end
class Customer
has_one :wallet
end
class Paperboy
def collect_money(customer, due_amount)
if customer.wallet.cash < due_ammount
raise InsufficientFundsError
else
customer.wallet.cash -= due_amount
@collected_amount += due_amount
end
end
end
paperboy should not be taking cash
out of a customer's wallet
18. Delegate attributes
class Customer
has_one :wallet
# attribute delegation
def cash
@wallet.cash
end
end
class Paperboy
def collect_money(customer, due_amount)
if customer.cash < due_ammount
raise InsufficientFundsError
else
customer.cash -= due_amount
@collected_amount += due_amount
end
end
end
paperboy doesn’t need to know how much
cash the client has in his wallet
19. Delegate Behaviour
class Customer
has_one :wallet
# behavior delegation
def pay(amount)
@wallet.withdraw(amount)
end
end
class Paperboy
def collect_money(customer, due_amount)
@collected_amount += customer.pay(due_amount)
end
end
Tell don’t ask
20. Summary
● It’s a guideline more than a law
● Look for the spirit of the law
● try.try just smells
Hint, it’s not a real gun
maintainable and adaptable code
objects are less dependent on the internal structure of other objects
object containers can be changed without reworking their callers.