Software Design/Code smells/Object access chain

Checklist questions:
 * Could an object access chain like  be encapsulated behind a single function call like   on object  ?

This is also known as the Law of Demeter or the "rule of one dot".

This code smell could be removed by applying Hide Delegate or Extract Function refactorings described in Refactoring.

Coupling and easiness of change
Code with object access chains depends on the classes of all intermediate objects in the chain. In case they are not common reusable data structures such as collections, this makes refactoring these classes harder, e. g. by amplifying the amount of code changes needed.

This factor is more important when the enclosing object is used outside of the codebase where it is defined, i. e. when it resides in a library. Hiding an object access chain behind a function allows refactoring the library and the dependant projects at different times. Refactoring of the API of the enclosing object may still be needed, however, to avoid divergence in terminology. For example, if there is a function  in some class and the concept of "monitoring" is renamed to "metrics", the class may still need to rename the function to   sooner or later, though it may give the dependant projects time to migrate, e. g. by deprecating the old function while adding a new one with the updated name.

If the enclosing object is used only within a single codebase and automated refactorings are possible, this factor may reverse: see section Change amplification below.

Information hiding and complexity
Encapsulating object access chains reduces the API surface of the enclosing object and thus makes it easier to test it and to reason about all possible state transitions of the enclosing object if the child object is mutable.

Code repetition
All occurrences of the same object access chain are repeated code. See practice Don't repeat logic in several places.

Cognitive load and semantics
Providing a function that encapsulates an object access chain reduces the cognitive load if the last function call in the chain changes the state of the respective object. When observing an object access chain, readers have to prove for themselves that the state-changing call doesn't break the abstraction provided by the previous objects in the chain. For example, consider the following code: When readers see the access chain  they may not be sure that the logic of classes   and   doesn't require to notify newly added listeners immediately about some events that may have already happened with an object , for example: Or, that the collection of Listeners in the Registry is not copy-on-write and therefore requires an update via a separate  operation: Developers may have to read the API documentation for classes  and , or their source code to clarify this question for themselves.

On the other hand, when developers see  readers may be sure (if they trust the developers of the   class) that function   handles everything that is needed for adding a listener to a registry.

Encapsulating an object access chain as a single function is an opportunity to give this function a descriptive (or a colorful) name which better reflects the semantics of the operation than an object access chain.

Debuggability
In programming environments with a null pointer, if one of the objects in the chain is null and null dereferencing occurs, it may be unknowable which exact dereferencing in the chain failed, making debugging harder. For example, in Java, this is the case until OpenJDK 14.

Discoverability
In the context of the example from section Cognitive load and semantics above, when a developer wants to add a listener to a registry, they may type  to discover the appropriate function via the autocompletion feature in their IDE. If there are intermediate objects in the chain, for example,  it's harder to discover the functionality.

Robustness in concurrent environment
Object access chain on a thread-safe object may expose parts of the object's state bypassing lock protection. If the exposed object is not immutable, there is a race condition possible. Thus, avoidance of object access chains on mutable objects used in a concurrent environment protects the code from a certain class of concurrency bugs, i. e. increases the robustness of the code.

Code and interface size, steepness of the learning curve
Functions that hide the object access chains are boilerplate. They increase the amount of code in the root class, as well as its API size, which may increase the steepness of the learning curve for the class.

Cognitive load, apparency of dependencies and semantics
Avoiding object access chains may increase the cognitive load imposed on users as well as decrease it (see section Why § Cognitive load and semantics above). Exposing parts of the state and functions on the sub-object through distinct functions on the root object may obscure the dependency between these parts of the state and functions, making the users check that for themselves in the API documentation or the source code of the class. Exposing a lot of weakly clustered functions on the root class may make grasping the abstraction embodied by it harder.

Consistency
If the object exposes object access to one of its parts anyway, providing access to other parts using object access chains rather than via encapsulating functions on the root class may be taken as a form of structural alignment in the code and the interface of the class.

Change amplification
When the enclosing object is used within a single codebase and automated refactorings are available, encapsulating object access chains within functions may only increase the change amplification. When the structure or names of objects in the chain are modified, this should may also need to be reflected in the name of the encapsulating function to preserve the consistency in naming.

When the codebase in question is a library or the automated symbol renames are not available in the programming environment, this factor may reverse: see the section Coupling and easiness of change above.

Using both approaches
Many of the factors contributing to why object access chains should be considered a code smell: information hiding and complexity, cognitive load and semantics, robustness in concurrent environment are largely (or completely) irrelevant when the leaf object in the access chain is immutable. On the other hand, an unprotected object access chain to a mutable object in some cases, e. g. in a concurrent environment is not just a matter of design but a bug.

Related

 * Don't repeat logic in several places