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​

Tangled
// 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.
With the pattern
// 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?