The law of good style

The law of good style

Why the Law of Demeter is crucial to object-oriented programming

GRASP, SOLID, DRY, KISS, IOC… Every developer worth their salt is familiar with this alphabet soup of best practices for programming. Which one do you like the most? SOLID (introduced by Robert C. Martin) is definitely the most popular one.

But if you want to know my favorite, you won’t find it in that list. I love the Law of Demeter. Why, you ask? I believe that the correct application of this law protects us from breaking most of the crucial principles of object-oriented programming.

The origin of the Law of Demeter

The “Law of Demeter” was defined by the Ian Holland in 1987 during his work on the Demeter project. The project was related to Adaptive Programming (AP) and Aspect-Oriented Programming (AOP). However, digging deeper, he realized that the law he created was useful for more than the limited the scope of the original project. In fact, it proved to be widely useful for all object-oriented programming.

The first name to be used in-house was the “The Law of Good Style.” The idea behind this law was to improve the style of the object-oriented code while minimizing the number of method arguments and methods in the class. Once implemented, this early iteration of the Law of Demeter was used to minimize duplications and guarantee better maintainability. It did this by enforcing proper encapsulation, modularization, and loose coupling.

It’s like your mother said, do not talk to strangers

Take this rather messy argument:

For all classes C, and for all methods M attached to C, all objects to which M sends a message must be instances of classes associated with the following classes:

  1. The argument classes of M (including C).
  2. The instance variable classes of C.

It looks very complex and hard to understand at a first sight. I’ll simplify it using the “law of one dot.” This means that in a single expression in the object-oriented code, only one dot is allowed. In this case, it’s ok to invoke,

user.submitOrder(order)

but you shouldn’t call

user.getOrders().submit(order)

Remember that we’re talking only about OOP. It’s perfectly fine to use as many dots as you want in functional programming.

Violations to avoid

Let’s take a look at an example which breaks the Demeter’s law:

human
    .getDigestiveSystem()
    .getPeritoneum()
    .getStomach()
    .add(Sausages.of(2))

Can you see what’s wrong with this code? There are a few problems.

The first problem is that it’s hard to read and understand. We would have to dig into the implementation details if we wanted to find out that the author has implemented the eating process. Without a deep knowledge of the business domain it is written for, it’d take ages to become good at modifying software written that way.

It’s also hard to test. Imagine the setup we would need to conduct the test. We’d need to mock a lot of different classes. So we‘d create a new human class with the injected mock of the DigestiveSystem. The call to the getPeritoneum method on a DigestiveSystem mock will return another mock. And a call on the Peritoneum.getStomach will return another mock - the Stomach. Finally, on the Stomach mock, we’d be able to verify if the two sausages were passed.

given:
  DigestiveSystem digestiveSystemMock = Mock(DigestiveSystem)
  Peritoneum peritoneumMock = Mock(Peritoneum)
  Stomach stomachMock = Mock(Stomach)

  Human human = new Human(digestiveSystemMock)

  when(digestiveSystemMock.getPeritoneum()).thenReturn(peritoneumMock)
  when(peritoneumMock.getStomach()).thenReturn(stomachMock)

when:
  // the code inlined for readability
  human
    .getDigestiveSystem()
    .getPeritoneum()
    .getStomach()
    .add(Sausages.of(2))

then:
  verify(stomachMock.add()).invoked()

I don’t know if you’ll agree with me, but I don’t like tests where the given section is a few times bigger than the when and then put together.

It’s hard to modify the process. Let’s say we would like to be able to handle vegan support. Before putting anything into the stomach, we’d need to verify if the person likes vegan food. With the current code, we’d need to identify all of the places in the application where we access and modify the Stomach. However, it’d still be hard to prevent a future situation where a freshly minted junior developer will put something into the belly without calling the proper if before.

It’s also hard to refactor this code, as it is extremely fragile. If we would like to simplify the human body by removing the Peritoneum it will cause compilation errors in all places where we access this class (or its components) for example, the Stomach.

Applying Demeter’s Law

Now let’s take a look at the core compliant to the Law of Demeter:

human.eat(Sausages.of(2))

Also, let’s take a look at the implementation of the eat method:

void eat(Food food) {
  digestiveSystem.swallow(food)
}

It looks much better now! To understand what the code does, all you have to do is take a look at the name of the method.

Testing? No problem. Just inject the DigestiveSystem mock into the human and verify if the swallow method has been invoked.

given:
  DigestiveSystem digestiveSystemMock = Mock(DigestiveSystem)
  Human human = new Human(digestiveSystemMock)

when:
  human.eat(Sausages.of(2))

then:
  verify(digestiveSystemMock.swallow()).invoked()

Adding support for tastes is also easy. Just add an if statement inside the eat method. No other access to the stomach is possible.

Refactoring? We won’t even notice the peritoneum had been removed. It’s properly encapsulated inside a digestive system.

The result

So applying one simple law helped us create readable, testable, and loosely coupled code. All you have to do is remember to avoid using more than one dot in an expression of object-oriented code.

So what’s your opinion? Do you follow the Law of Demeter on a daily basis?

Head image: https://www.flickr.com/photos/frosted_peppercorn/205501453

Jakub Kubrynski's Picture

About Jakub Kubrynski

Jakub is a DevSkiller co-founder. He is a software developer for whom building software is a way of life as well as a passion. He is focused on continuously improving software delivery processes by introducing new technologies and refining Lean methodologies. For over 12 years of his professional career, he worked as a software developer, architect, team leader, and manager. He gained experience working on both sides of the delivery process, as a vendor and as a client.

https://twitter.com/jkubrynski