22

I have this code in some part of an application:

long sum1 = new Multiples().ofAny(new long[] { 3, 5 }).until(32768).sum();
long sum2 = new Multiples().ofAll(new long[] { 3, 5 }).until(32768).sum();
long sum3 = new Multiples().of(32).until(4096).sum();

I created it so readers have a clear vision of what's happening, but each method call returns a different object of a different type (Multiples -> MultiplesCalculator -> MultiplesCalculationResult -> long).

In other words, I am doing A -> B -> C -> D, while Law of Demeter (LoD) recommends only A -> B

Is this a valid use case to break LoD?

10
  • 16
    Clarity is always more important than any rule of thumb. If you think this is the clearest way to write it, then use it. – Gabe Sechan Jan 11 at 5:07
  • 4
    I am having trouble seeing how any of this relates to the law of Demeter, which essentially tells us to control scope. There is no scope concern anywhere here; indeed, it appears all of your methods are pure functions. – John Wu Jan 11 at 8:39
  • 4
    Perhaps we should change the question to "Does this scenario violate the Law of Demeter?" – Rik D Jan 11 at 9:03
  • 9
    I just wanted to mention that I like the API of your Multiple calculator, I think it's very readable. One nitpick: If the only purpose of your Multiples class is to provide those of... methods, you might consider making them static. long sum1 = Multiples.of(32).until... reads even smoother and the object instance does not seem to serve any purpose. – Heinzi Jan 11 at 12:32
  • 14
    It looks like you have a fluent interface. Do fluent interfaces violate the Law of Demeter? – Pål GD Jan 11 at 12:59
34

The Law of Demeter (AKA The Principle of Least Knowledge) says that it's better to only talk with your friends. It prohibits talking to friends of friends. The reason why is because if you randomly delve into a code base and link together any old random things then you turn what was flexible code into a tangled mess that can't be easily teased apart.

Talking only to your friends limits what you know about and so limits what you care about when change comes. It's nice when a code base can accept a change without forcing you to go fix code in a dozen different packages.

This is what people mean when they say LoD is not a dot counting exercise. It's not a LoD violation just because of the number of dots. What matters is what you're talking to. So long as the dots only take you to your friends you can dot as much as you like.

LoD is fine with what you're doing provided Multiples, MultiplesCalculator, and MultiplesCalculationResult are all friends. That is, they are likely to change together and be deployed together.

What you've created is called an iDSL/eDSL. It's basically a mini language all it's own. They are very powerful but can be a pain to set up. Create them when they will be used often to offset the creation cost.

3
  • 15
    More precisely, it is an iDSL using a Fluent Interface based on a Type-Safe Builder. – Jörg W Mittag Jan 11 at 7:55
  • I don't think this counts as a Fluent Interface. According to my understanding, FI requires fluent methods to return an object of the same type as the original object (in the case of mutable data structures, usually the object itself). Otherwise one could argue that the entire Python language trivially implements a fluent interface, given that all values are objects, and so all values have methods that return objects, therefore one could chain method calls all day, on any value. – Jasmijn Jan 11 at 14:54
  • 7
    @Jasmijn Nothing in a fluent interface says that the type needs to stay the same. On the contrary, it is often better to have different types as to restrict which methods can be called in what sequence. A fluent interface only requires that you return something useful that you can continue working with. (In the case of mutating data structures, that must be the object itself, as otherwise the mutated object is lost...) – Bergi Jan 11 at 15:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy