What is the Law of Demeter (also called the "principle of least knowledge") in object-oriented design, and what is a "train wreck" call chain?
answer
- Only talk to immediate friends
- Don't talk to strangers
- a.getB().getC().do() = train wreck
- Mocks returning mocks = smell
- Tell, don't ask beats Hide Delegate
basics
~10 sA design guideline: an object should only call methods on its immediate collaborators, not reach through them into strangers. A "train wreck" is a chain like order.getCustomer().getAddress().getCity().getName() that navigates someone else's object graph.
solid answer
~50 sThe Law of Demeter (LoD), or principle of least knowledge, says a method should only send messages to objects it directly knows: itself, its own fields, its parameters, objects it creates, and (in the original phrasing) globals in scope. It must not call a method on an object it got back from one of those calls — a "stranger". A train wreck is the visible symptom: order.getCustomer().getAddress().getCity().getName(). The calling code now depends on four types and on the exact shape of the graph connecting them. Any change — Address gains a nullable City, Customer stops owning Address directly — breaks callers that never should have known about it. LoD trades this structural coupling for delegation: the caller asks order.getCustomerCity() (or better, tells the order to do the work) and the knowledge of the graph stays where it belongs.
code
pseudocode · 8 lines// Train wreck: caller knows Order -> Customer -> Address -> City
name = order.getCustomer().getAddress().getCity().getName()
// Delegation: each hop hidden behind one method
name = order.customerCityName()
// Better still - tell, don't ask: caller wanted an outcome, not data
order.printShippingLabel(printer)go deeper
Name it (principle of least knowledge), give the train-wreck example, say the fix is to ask your direct collaborator for what you need.
Add the concrete costs — ripple change, mock-of-mock tests, null handling leaking to callers — and mention Hide Delegate as the mechanical refactoring.
Frame it as controlling structural coupling to the object graph, escalate from delegation to Tell-Don't-Ask, and note the middle-man cost of applying it dogmatically.
Position it as one point on a coupling spectrum: it is a heuristic, doesn't apply to data structures, and its real value is that it keeps knowledge of graph shape from spreading across a codebase or across service boundaries.
## The core idea The Law of Demeter came out of the Demeter research project at Northeastern University (Karl Lieberherr, Ian Holland, ~1987). Despite the name, it is a **heuristic**, not a law — a style rule you apply with judgement. Its informal statements: - **"Only talk to your immediate friends."** - **"Don't talk to strangers."** - **"Principle of least knowledge"** — a unit should know as little as possible about the structure of anything other than itself. ### Terms, defined - **Object**: a bundle of data plus the operations on it. - **Method / message send**: calling an operation on an object (`a.doThing()` sends the message `doThing` to `a`). - **Collaborator**: another object that this object holds or receives, and uses to get its job done. - **Stranger**: an object you obtained *indirectly* — it was handed back to you by a collaborator. You didn't create it, don't hold it, weren't given it. - **Coupling**: how much one piece of code must know about another to work. **Structural coupling** here means knowing the *shape of the object graph* — who holds whom. ### The train wreck ``` city = order.getCustomer().getAddress().getCity().getName() ``` It's called a train wreck because the chained calls look like coupled railway cars. This one line encodes a claim: *an Order has a Customer, which has an Address, which has a City, which has a name.* Four types, three edges of the graph. The compiler will not warn you when that claim stops being true; you just get a cascade of edits, or a null dereference at runtime because one link in the chain became optional. ### Why it hurts 1. **Ripple effect on change.** Replace `Address` with a `PostalLocation`? Every caller that walked through it changes, even though those callers only ever wanted a city name. 2. **Testing pain.** To unit-test the caller you must build (or mock) the entire chain — a mock returning a mock returning a mock. "Mocks returning mocks" is a well-known LoD alarm bell. 3. **Null / absence handling leaks.** Each hop is a place something can be missing, and the *caller* — who has no business knowing — has to handle it. 4. **Encapsulation is defeated.** `Customer` declared a private field and then handed it out through a getter. Privacy of the *field* was preserved; privacy of the *design* was not. 5. **Logic migrates outward.** Behaviour that belongs inside `Order` or `Customer` accumulates in callers instead — the Feature Envy smell. ### Fixing it: two levels **Weak fix — delegation ("Hide Delegate" refactoring):** ``` order.getCustomerCity() // Order asks Customer, Customer asks Address, ... ``` Each object exposes what the next one out needs and hides the hop. Coupling now goes one edge at a time. **Strong fix — Tell, Don't Ask:** Often the caller didn't want the city name at all; it wanted to *do* something with it. Instead of pulling data out, push the behaviour in: ``` order.printShippingLabel(printer) ``` Now no data is extracted, no chain exists, and the knowledge stays with the object that owns it. ### What LoD is not - It is **not** "one dot per line". Fluent builders (`builder.withA().withB().build()`) and collection/stream pipelines (`items.filter(...).map(...).sum()`) chain heavily and are usually fine, because each call returns the *same conceptual object* or a new value, not a deeper node of someone else's graph. - It is **not** free. Applied dogmatically it produces armies of pass-through wrapper methods (the "middle man" smell) — a real cost you must weigh. - It applies most strongly to **objects with behaviour**. Pure data structures / DTOs / configuration trees / parsed JSON deliberately expose structure; navigating them is their point. ### Rule of thumb Ask: *does this line depend on how another object is built internally, or only on what it can do?* If the former, you're likely violating LoD.
- Does using a getter automatically violate the Law of Demeter?No. Calling a getter on an object you directly hold is fine. The violation is calling a method on the *result* of that getter — that result is a stranger you reached through your collaborator.
- Why is 'mocks returning mocks' in a test considered evidence of an LoD violation?Because the code under test navigates a chain it doesn't own, so the test must reconstruct that chain. If the test only had to stub one direct collaborator, the production code would only be talking to friends.
You want cash from a shop customer. You don't reach into their pocket, pull out their wallet, and take a note from it — you ask the customer to pay. You depend on what they can do (pay), not on whether their money lives in a wallet, a phone, or a sock.