Skip to main content
Create your own
Lesson illustration

Demeter's Law: Reduce Coupling

Hello! Welcome to your next lesson in the Fundamental Design Principles module.

In our last session, we focused on achieving low coupling by preferring composition over inheritance. We saw that building systems out of collaborative, independent objects ("has-a") leads to more flexible and maintainable code than rigid "is-a" hierarchies.

Now that we are composing systems from many collaborating objects, a new question arises: how should these objects talk to each other without reintroducing the tight coupling we worked so hard to avoid?

Today's lesson answers that question by introducing the Law of Demeter (LoD), also known as the Principle of Least Knowledge. This principle provides a clear guideline for how objects should interact, ensuring that our loosely coupled components stay that way. It's a fundamental concept that interviewers look for when assessing a candidate's LLD skills.

Our goal for this lesson is to apply the 'Law of Demeter' to reduce coupling in our designs.

1. The Principle of Least Knowledge

At its core, the Law of Demeter is simple. It's often summarized with the phrase: "Only talk to your immediate friends." An object should have limited knowledge about other objects; it should only interact with objects that are closely related to it and avoid talking to "strangers" (objects reached through other objects).

Let's start with a high-level video that introduces this idea in the context of system components.

Lesson 57 - The Law of Demeter

The video 'Lesson 57 - The Law of Demeter' from the Software Architecture Monday channel provides an excellent introduction to this principle from an architectural perspective.

Watch the video from the beginning to 06:13. Pay attention to: The initial example where the 'Order Placement' component knows too much about the overall workflow (0:00 - 1:44). The three simple rules: talk to friends, don't talk to strangers, only talk to immediate friends (1:44 - 3:01). How decoupling is achieved by moving knowledge and responsibility to the most appropriate component (3:01 - 6:13).

As the video shows, when the Order Placement service knows it has to call the Supplier Ordering and Item Pricing services, it becomes tightly coupled to the system's business process. By delegating that knowledge to the Inventory Management service, the Order Placement service becomes simpler and less coupled to the overall system. It knows less, which makes it more robust.

This principle can be visualized quite simply:

Law of Demeter Illustration
This diagram illustrates the core idea of the Law of Demeter. An object `A` can talk to its immediate neighbor `B`, and `B` can talk to its neighbor `C`. However, `A` should not bypass `B` to talk directly to `C`. It should ask `B` to handle the communication.

2. The "Train Wreck": Violating the Law of Demeter

The most common and obvious violation of the Law of Demeter is a chain of method calls, often called a "train wreck" because of the long series of dots (.). This is a strong code smell indicating high coupling.

Let's look at a classic e-commerce example that makes this problem very clear.

Lod - Law of Demeter

The article 'Lod - Law of Demeter' from algomaster.io brilliantly illustrates the problem with a 'train wreck' code snippet and explains its negative consequences.

Read the sections 'The Problem' and 'What’s Wrong With This?'. Focus on the example customer.getShoppingCart().getItems().get(0)... and understand why this is so problematic in terms of coupling, encapsulation, maintenance, and testability.

As the article explains, the line customer.getShoppingCart().getItems().get(0).getProduct().getPrice() couples the OrderService to the internal structure of Customer, ShoppingCart, CartItem, and Product. A small change in any of these classes could break the OrderService, even if the change is logically unrelated to placing an order.

This is not just a theoretical problem. In your work with Spring Boot, you may have seen code that navigates through several layers of entities like this. It works, but it creates a maintenance nightmare. A change to one database entity can cause a ripple effect of changes across many unrelated services.

3. Refactoring to Adhere to the Law

The solution is to stop reaching through objects and instead delegate responsibility. The object that has the information should also have the behavior related to that information.

We can fix the "train wreck" by asking the Customer object for the information we need directly, and letting it handle the internal details.

Law of Demeter: Coffee Machine Example
This simple analogy shows the difference. **Bad:** The person reaches through the cup to find the machine to get the price. **Good:** The person simply asks the cup for its cost, and the cup figures it out internally by talking to its creator, the coffee machine.

Now, let's see how to apply this refactoring to our code example.

Lod - Law of Demeter

Let's continue with the same algomaster.io article to see the step-by-step refactoring.

Read the section 'Refactoring with LoD in Mind'. Notice how new methods like getFirstProductPrice() are added to Customer and ShoppingCart. The OrderService now makes a single, clean call: customer.getFirstProductPrice().

The refactored code is much better. The OrderService is now only coupled to the Customer class's public interface. It has no knowledge of ShoppingCart or Product. The internal implementation of how a customer's first item price is calculated can change completely without affecting the OrderService. This is the power of low coupling.

4. The Formal Rules of the Law in Java

The "talk to immediate friends" idea can be formalized into a set of concrete rules. For a method in a class, it should only call methods on:

  1. Itself (this)
  2. An object it creates (new ...)
  3. An object passed in as a parameter
  4. An object held in an instance variable (a field of the class)
  5. A static field

Let's explore these rules with specific Java examples.

Law of Demeter in Java

The article 'Law of Demeter in Java' from Baeldung provides clear Java code snippets for each of these rules, making the principle very concrete.

First, read section 2, 'Understanding the Law of Demeter', which lists the five rules. Then, read section 3, 'Examples of the Law of Demeter in Java', which shows a simple code example for each rule. Finally, read section 4, 'Violating the Law of Demeter', to see a before-and-after example with Employee, Department, and Manager classes.

The Baeldung example, employee.getDepartment().getManager().approveExpense(expenses), is another perfect illustration of a violation. The fix, which involves giving the Employee a direct reference to a Manager and a submitExpense method, demonstrates how to refactor to adhere to the law.

Test your understanding!

You are working on a user management system. You have the following class structure:

class Address {
    private String city;
    public Address(String city) { this.city = city; }
    public String getCity() { return city; }
}

class UserProfile {
    private Address address;
    public UserProfile(Address address) { this.address = address; }
    public Address getAddress() { return address; }
}

class User {
    private UserProfile profile;
    public User(UserProfile profile) { this.profile = profile; }
    public UserProfile getProfile() { return profile; }
}

// Client code
public class ProfileService {
    public void displayUserCity(User user) {
        // This line violates the Law of Demeter
        String city = user.getProfile().getAddress().getCity();
        System.out.println("User city: " + city);
    }
}

The line user.getProfile().getAddress().getCity() is a "train wreck." How would you refactor the User, UserProfile, and ProfileService classes to comply with the Law of Demeter? The ProfileService should be able to get the user's city with a single method call on the user object.

Show answer

To fix the violation, we need to apply delegation. We will add methods to User and UserProfile to hide the internal structure from ProfileService.

Step 1: Add a delegation method to UserProfile

The UserProfile knows about the Address, so it should be responsible for getting the city from it.

class UserProfile {
    private Address address;
    public UserProfile(Address address) { this.address = address; }
    public Address getAddress() { return address; } // This getter can remain for other purposes

    // New delegation method
    public String getCity() {
        return address.getCity();
    }
}

Step 2: Add a delegation method to User

The User knows about the UserProfile, so it can now delegate the call to its profile.

class User {
    private UserProfile profile;
    public User(UserProfile profile) { this.profile = profile; }
    public UserProfile getProfile() { return profile; } // This getter can also remain

    // New delegation method
    public String getCity() {
        return profile.getCity();
    }
}

Step 3: Refactor the ProfileService

Now, the ProfileService only needs to talk to its "immediate friend," the user object.

public class ProfileService {
    public void displayUserCity(User user) {
        // This line now complies with the Law of Demeter
        String city = user.getCity();
        System.out.println("User city: " + city);
    }
}

The ProfileService no longer knows that a User has a UserProfile, or that a UserProfile has an Address. It is decoupled from that implementation detail.

5. Benefits, Nuances, and Exceptions

Applying the Law of Demeter leads to systems that are easier to maintain and test.

Benefits:

  • Low Coupling: Changes in one class don't ripple through the codebase.
  • Better Encapsulation: Each class manages its own internal state and logic.
  • Improved Testability: Tests are simpler because you don't need to mock long chains of dependencies.
  • Cleaner APIs: Public methods become more expressive and task-oriented.

However, LoD is a guideline, not an absolute rule. Blindly applying it can sometimes lead to a proliferation of simple wrapper methods. It's important to understand the trade-offs and common exceptions.

Lod - Law of Demeter and Law of Demeter in Java

Both the algomaster.io and Baeldung articles discuss important exceptions where method chaining is acceptable.

First, read the 'Common Questions About LoD' section from the algomaster.io article. It addresses key concerns like writing extra 'wrapper' code and how LoD applies to getters and data structures.

Law of Demeter in Java

Next, read the 'Exception to the Law of Demeter' section from the Baeldung article. It specifically calls out acceptable chaining in the Builder pattern and Fluent APIs, which are common in modern Java development.

Key exceptions where method chaining does not typically violate the spirit of LoD include:

  • Fluent APIs and Builders: Calls like new StringBuilder().append("a").append("b").toString() are fine because each method call (except the last) returns this or a locally created object.
  • Data Transfer Objects (DTOs): Objects that are pure data structures with no behavior are often exempt. The goal of LoD is to encapsulate behavior, and DTOs have none.

Conclusion

The Law of Demeter is a powerful tool for maintaining low coupling in object-oriented systems. By ensuring that objects only talk to their immediate friends, you prevent implementation details from leaking out and creating fragile, interconnected code. This forces you to think more carefully about where responsibility should lie, leading to better-encapsulated and more maintainable designs.

Key Takeaways:

  • Core Principle: "Only talk to your immediate friends." An object should have minimal knowledge of the structure of other objects.
  • Avoid "Train Wrecks": Long chains of method calls (a.getB().getC().doSomething()) are a major red flag for high coupling and a violation of the Law of Demeter.
  • Delegate, Don't Reach: Instead of reaching into an object to get what you need, ask the object to perform the task for you by giving it a method.
  • It's a Guideline: Understand the exceptions, like fluent APIs and builders, where method chaining is an intentional and acceptable design choice.

In our next lesson, we will explore the 'Tell, Don't Ask' principle. You've already seen it in action today! Refactoring to follow the Law of Demeter is a direct application of this principle. Instead of asking an object for its data and then acting on that data, we tell the object what to do. We'll dive deeper into this concept to further improve encapsulation in our designs.

Can't find a good explanation? Sign up and we'll make it for you

Sign up