Skip to main content

Law of Demeter

Talk to your immediate friends. Don't talk to strangers you only met through a friend.

A method may call operations on itself, on its own fields, on anything handed to it as a parameter, and on anything it creates on the spot. It may not call an operation on whatever some other call happened to hand back - that return value is a stranger, and reaching past it into a third object's business is the "train wreck" this principle is named for.

Why it exists

customer.getWallet().getCash().subtract(amount) compiles, works today, and quietly commits OrderService to three facts it has no business knowing: that a Customer has a Wallet, that a Wallet holds Cash, and that Cash supports subtract(). Change any one of those three facts - Wallet switches from cash to a card balance, say - and this unrelated service breaks, even though nobody touched OrderService on purpose. The chain of dots is a chain of hidden dependencies, one per dot past the first.

A train wreck, and its fix

// OrderService reaching three objects deep to do one thing.
method chargeCustomer(customer, amount) is
customer.getWallet().getCash().subtract(amount)
 
// This line knows that a Customer has a Wallet, that a Wallet
// holds Cash, and that Cash supports subtract(). Change any one
// of those three facts and this unrelated service breaks.
// OrderService talks to one friend: the Customer it was handed.
method chargeCustomer(customer, amount) is
customer.pay(amount)
 
method Customer.pay(amount) is
wallet.pay(amount)
 
method Wallet.pay(amount) is
cash.subtract(amount)
 
// Each hop only knows the one object right next to it. Wallet
// can switch from Cash to Card tomorrow and OrderService, which
// never knew Wallet held cash in the first place, feels nothing.

The fix is never "add more getters so the caller can keep reaching." It's the opposite: push the behavior down to whichever object already holds the data, and let the caller ask for the result instead of the parts. This is sometimes called "tell, don't ask" - tell the wallet to pay, don't ask it for its cash so you can subtract from it yourself.

Try it yourself: report.getCustomer().getAddress().getCity().toUpperCase() shows up in a report footer. Rewrite it so report has exactly one thing to ask for, and decide which object - Customer or Address - should own the formatting.

The cost of overapplying it

Taken too literally, Law of Demeter turns into "wrap every getter in a pass-through method," and a class ends up with a dozen one-line delegating methods that add a hop without adding any actual behavior - the "middle-man" anti-pattern. The principle is about hiding structure the caller shouldn't depend on, not about banning every method that happens to return an object. order.getLineItems() returning a plain list for a caller to iterate is fine; the violation is specifically reaching through that list into a third object's internals as if you owned them.

How it relates to its neighbours

The train wreck is a coupling problem wearing a more specific name. A chain of four dots is four separate objects that a change anywhere can break - exactly the failure Coupling and Cohesion warns about, caught in one particular, grep-able shape: a line with more than one dot past the first.

Where you'll see it in the pattern catalog

Facade exists partly to give callers a Law-of-Demeter-friendly front door instead of reaching through a subsystem's internals to assemble a result themselves. Mediator solves the same family of problem one level up: instead of components chaining calls through each other to coordinate, they all talk to one hub, and no chain of dots ever forms in the first place.

Check yourself

Question 1 of 3

customer.getWallet().getCash().subtract(amount) compiles fine and works today. What did it actually violate, and why does it matter later?