skip to content

What does the Law of Demeter state, and why is a chain like order.getCustomer().getAddress().getCity().toUpperCase() considered a problem?

level: middleimportance: must knowfreq 70%

answer

  1. Only talk to friends, not strangers
  2. Allowed: this, own fields, parameters, things you created
  3. Train wreck = a.getB().getC().getD()
  4. Tell, Don't Ask beats adding wrapper getters
  5. Data structures, builders and streams are exempt

basics

~20 s

The Law of Demeter says a method should only talk to its immediate neighbours: itself, its own fields, its parameters, and objects it created. Long chains like that one couple your code to the internal structure of three other classes, so any of them changing breaks you.

solid answer

~50 s

The Law of Demeter ("only talk to your friends, not to strangers") restricts which objects a method may send messages to: itself, its own fields, its parameters, objects it creates, and globals it holds. It must not navigate through objects returned by other calls. `order.getCustomer().getAddress().getCity()` - a *train wreck* - violates this: the caller now knows an order has a customer, a customer has an address, and an address has a city. Three classes it never asked for become part of its compile-time and change-time coupling; any of them may also return null, and tests must build the whole graph as mocks-returning-mocks. The fix is *Tell, Don't Ask*: push the behaviour toward the data (`order.shippingCity()` or, better, `order.ship(shipper)`). Two important caveats: the law constrains *objects*, not data structures - chaining through public fields of dumb records or through a fluent builder/stream API is not a violation - and mechanically adding wrapper getters at every level ("Demeter delegation bloat") satisfies the letter while missing the point.

code

pseudocode · 6 lines
pseudocode
// Train wreck: caller knows Order -> Customer -> Address -> City
city = order.getCustomer().getAddress().getCity()
label = city.toUpperCase()

// Tell, Don't Ask: move the operation to the data
order.printShippingLabel(printer)

go deeper

for a junior

Say what is allowed to be called (this, fields, parameters, created objects) and that long getter chains couple you to classes you never asked about.

for a middle

Name train wrecks, explain the coupling/null/test consequences, and give the Tell-Don't-Ask fix rather than pass-through getters.

for a senior

Distinguish objects (constrained) from data structures, builders, and value chains (exempt); discuss delegation bloat and when to relocate behaviour instead.

for a principal

Treat it as a coupling-management heuristic: use it as a smell detector for misplaced responsibility and module boundary violations, and be explicit about where the team relaxes it (edge DTOs, generated clients, test fixtures).

## The rule The Law of Demeter (LoD), also called the *principle of least knowledge*, was formulated at Northeastern University in the late 1980s. Its common object-oriented form: a method `m` of class `C` may only invoke methods of 1. `C` itself, 2. objects held in `C`'s own fields, 3. objects passed to `m` as parameters, 4. objects `m` creates itself, 5. globals/singletons accessible to `C`. Crucially it may **not** invoke methods on objects *returned by* those calls. That is the whole rule: one dot's worth of navigation from something you already legitimately hold. ## Train wrecks `a.getB().getC().getD().doThing()` is called a *train wreck* because the calls are coupled like railway cars. What is actually wrong: - **Structural coupling.** The caller encodes the shape of a graph it does not own. Change `Address` so the city lives on a `Locality`, and every caller in the codebase breaks - even ones that only cared about shipping. - **Broken encapsulation.** Each `getX()` in the chain leaks a collaborator. The intermediate types are implementation detail promoted to public API. - **Null / absence handling.** Every link may be absent, so callers either risk a null-dereference or write defensive ladders that duplicate the same knowledge everywhere. - **Test pain.** Unit testing requires mocks returning mocks returning mocks - a classic sign that responsibilities sit in the wrong place. - **Feature envy.** The logic being written is more interested in another object's data than in its own, which is the smell that says the method belongs elsewhere. ## The real fix: Tell, Don't Ask Don't ask an object for its parts to make a decision - tell it to make the decision or do the work. - Weak fix: add `order.getShippingCity()` that delegates internally. Legal, and fine if "shipping city" is genuinely part of the Order abstraction. - Better fix: move the *operation*, not the *data*: `order.ship(shipper)`, `invoice.printTo(printer)`, `account.canCover(amount)`. - Systemic fix: check whether the caller needs that graph at all, or whether the wrong module is doing the work. Blindly adding a pass-through getter at every hop just to remove dots is **Demeter delegation bloat**: the API doubles in size, coupling is unchanged, and nobody gained anything. ## Important exemptions - **Data structures are exempt.** LoD constrains objects that hide their representation. If `Point`, a JSON tree, or a config record is openly a data structure, `config.server.tls.port` is honest navigation, not a violation. Martin's own framing: "the Law of Demeter says nothing about data structures." - **Fluent interfaces / builders / streams.** `builder().name(x).age(y).build()` or `list.filter(...).map(...).toList()` return *the same conceptual thing* (the builder, a new collection), not deeper collaborators. No hidden structure is being traversed. - **`this`-returning chains** and standard library value chains (`text.trim().toLowerCase()`) are similarly fine - each step returns a value of the same abstraction level, not an internal part. ## Cost side LoD is a heuristic, not a theorem. Enforced dogmatically it produces many small delegating methods and can push unrelated responsibilities onto middle objects. The judgement call: does the delegating method express something the middle object *should* know? If yes, delegate. If no, you are hiding a misplaced responsibility, and the right move is to relocate the behaviour or restructure the collaboration.

  • Does list.stream().filter(p).map(f).toList() violate the Law of Demeter?
    No. Each step returns the same conceptual abstraction (a stream/collection), not an internal collaborator of another object. The law targets navigation through the structure of objects that are supposed to hide it.
  • Is adding a delegating getter at each level always the right fix?
    No - that is delegation bloat. It removes dots without removing coupling and inflates every intermediate API. Prefer moving the behaviour to where the data lives, and only delegate when the method genuinely belongs to that object's abstraction.

You pay a shop, not the shop owner's wallet's cash. Reaching through the cashier into the till to make change yourself is exactly what a train wreck does.

saying these in an interview costs you the question

  • Restating it as 'never use more than one dot' - method count, not dot count, and value/fluent chains are fine
  • Applying it to plain data structures, DTOs, or configuration trees
  • Fixing violations by mechanically generating pass-through getters everywhere
  • Claiming null-safe navigation operators solve the design problem (they only hide the null, not the coupling)
  • Thinking it is a compiler-enforced law rather than a heuristic with real costs

context