design – Law of Demeter and over-wide interfaces

# better
dog.walk()

# worse
dog.legs().front().left().move()
dog.legs().back().right().move()
# etc.

There are two reasons why the second is worse. The first, not really directly LOD-related, is that your walk logic isn’t reusable, which is a problem in the case where there are multiple places in your codebase where a dog must walk.

The LOD-related reason why this code is bad is because it forces the current consumer to know that DogLeg exists and how to operate it. The unspoken expectation here is that your consumer only knows about a Dog, and that it can be made to move around, but how that Dog moves around isn’t something the consumer cares about (that’s up to the Dog to manage for themselves).

However, that is not necessarily the case for your other example.

account.user().fullName()

If Account and User are both domain objects of which your consumer has public knowledge, then there’s no issue with asking them to handle a User object directly.
The expectation here is that “the account refers to its owner” is part of the Account interface, and therefore returning the owner (represented by a User object) is fair game.

Comparatively, your Dog interface is not expected to include “the dog has legs”, but rather “the dog is able to move around“, and the legs are just an implementation detail so the dog is able to fulfill its contract (i.e. moving around). The interface itself doesn’t specify the existence of legs, and therefore the consumer of Dog shouldn’t be relying on the existence of legs.

In essence, a DogLeg is considered a private implementation detail, whereas a User (class) is publically known. This means that there’s significantly less issue with expecting your consumer to handle a User than there is with expecting them to handle a DogLeg.

That being said, if account.user() was actually an AccountUser object which would also be considered a private implementation detail, then the same principle applies as it does for DogLeg.

This is what makes LOD so tricky to pinpoint. It’s not something that is objectively true based on your code alone, it hinges on subjective context and expectation of interfaces/contracts. By renaming the code, you change the reader’s implicit expectation, which can change whether something is considered an LOD violation.

dog.legs().front()
account.user().fullName()

Technically, it’s the same code. But what changes is our expectation of how acceptable it is to force a consumer to directly handler a DogLeg vs forcing them to handle a User.