From dev to prod logo
Published on
-

The Law of Demeter, Writing Cleaner and More Maintainable Code 😊

6 min read - 1054 words
Authors
  • avatar
    Name
    CΓ©dric RIBALTA
    Twitter
post image

Introduction

As a developer, you've probably heard about best practices for writing maintainable and scalable code.

Person with glasses, shocked and saying what?

If not, you can start your journey with an article on the ten characteristics of a clean function, it's a good first step.

Among these practices, one of the most important is The Law of Demeter, also known as the Principle of Least Knowledge. It is often mentioned in reference books such as the famous Clean Code by Robert C. Martin, aka Uncle Bob.

But what does this law really mean, and how can it help you write cleaner and more solid code? πŸ€”

What is the Law of Demeter?

The Law of Demeter is based on a simple concept: an object should only interact with its immediate friends, meaning the objects it knows directly. In other words, it forbids method chains on objects that are not directly accessible.

This rule can be summed up with the phrase: "Only talk to your direct friends." πŸ‘₯

In practical terms, this means that instead of letting an object interact with internal objects by accessing sub-objects, it should delegate those interactions to the responsible immediate object. This principle helps decouple classes from one another, making the code easier to maintain. πŸ”§

You can also combine the Law of Demeter with the Dependency Inversion Principle to get even more robust code.

Why is it important?

Applying the Law of Demeter to your code allows you to:

  • Reduce coupling: Fewer dependencies between classes make your code more robust to changes. πŸ’ͺ
  • Improve readability: Avoiding method chains makes the code easier to read and understand. πŸ‘“
  • Simplify unit testing: Loosely coupled objects are easier to test because each object focuses only on its own responsibilities (and it's even better if each function does one thing). βœ…

Detailed Benefits of the Law of Demeter

  • Reducing coupling: The main advantage of the Law of Demeter is reducing the coupling between objects. By limiting interactions to what is strictly necessary, your code becomes more modular and independent. This makes future modifications or additions easier without the risk of breaking existing functionality (combine with the Open/Closed Principle). Imagine you change the internal structure of an object like Engine; if only the object itself knows its structure, changes won't directly affect other objects like Car.

  • Resilience to changes: The less coupled your code is, the less it will be affected by changes in other parts of the system. Tightly coupled code, on the other hand, tends to propagate the effects of changes across many objects, making modifications risky and time-consuming (in general, you should be careful with side effects). πŸ•’

  • Code clarity: When code is written following the Law of Demeter, it becomes easier to read. Complex method chains, often a source of confusion, are avoided. This not only makes your code easier to maintain but also more accessible for new developers joining your project. ✨

Example of Violating the Law of Demeter

Let's take a TypeScript example to illustrate a common violation of the Law of Demeter.

class FuelTank {
  getFuelLevel(): number {
    return 80 // Fuel level
  }
}

class Engine {
  fuelTank: FuelTank

  constructor(fuelTank: FuelTank) {
    this.fuelTank = fuelTank
  }

  getFuelTank(): FuelTank {
    return this.fuelTank
  }
}

class Car {
  engine: Engine

  constructor(engine: Engine) {
    this.engine = engine
  }

  getEngine(): Engine {
    return this.engine
  }
}

const myCar = new Car(new Engine(new FuelTank()))
console.log(myCar.getEngine().getFuelTank().getFuelLevel()) // Bad!

Here, the Car object directly accesses the fuel tank (FuelTank) through a series of method calls. This is a violation of the Law of Demeter, as Car should not know the internal details of Engine or FuelTank.

Respecting the Law of Demeter

To respect the Law of Demeter, you should modify the Car class to delegate the fuel level retrieval to the Engine class:

class Car {
  engine: Engine

  constructor(engine: Engine) {
    this.engine = engine
  }

  getFuelLevel(): number {
    return this.engine.getFuelTank().getFuelLevel()
  }
}

const myCar = new Car(new Engine(new FuelTank()))
console.log(myCar.getFuelLevel()) // Good!

In this version, the Car class no longer directly accesses the FuelTank object. Instead, it delegates this responsibility to the Engine class, which respects the Law of Demeter.

Impact on Unit Testing

Applying the Law of Demeter also improves the testability of the code. Why? Because loosely coupled code is easier to isolate when writing unit tests (which should represent 70% of your testing pyramid). πŸ”

  • Testing objects in isolation: An object that respects the Law of Demeter only depends on its immediate relationships, which makes it easier to create mocks or fakes during unit testing . If an object Car only needs to know about its engine (Engine), then unit testing can focus solely on that direct relationship without worrying about internal details like the fuel tank (FuelTank).

  • Reducing fragile tests: Method chains can lead to fragile tests because a change in an internal class can break several tests at once. By following the Law of Demeter, you minimize these risks since each class manages its own responsibilities, and internal changes remain confined to the concerned class. 🚦

The Law of Demeter is often compared or linked to other design principles, notably the Single Responsibility Principle (SRP), which states that a class should have only one responsibility. Following the Law of Demeter contributes to this idea by ensuring that each class manages only what belongs to it.

  • Single Responsibility Principle (SRP): By decoupling class responsibilities, you simplify their management and ensure that they only handle one thing. For instance, if Car only knows about the engine (Engine) and doesn't directly interact with the fuel tank (FuelTank), it reinforces the SRP principle. πŸ› οΈ

  • Interface Segregation Principle (ISP): The Law of Demeter also encourages separating interfaces and minimizing interactions with objects that shouldn't be directly related. This aligns with the idea that interfaces should be reduced to only what is strictly necessary to avoid unnecessary dependencies.

Conclusion

Applying the Law of Demeter to your code helps reduce coupling, improve readability, and simplify unit testing. While it may be tempting to chain method calls to access internal objects, this creates excessive coupling that makes the code fragile to future modifications.

Next time you see method chains in your code, ask yourself: Am I violating the Law of Demeter? 🀨 By encapsulating interactions and reducing object dependencies, you'll make your code more robust and scalable. πŸš€

As our dear friend Judge Dredd would say:

Judge Dredd saying I am the law