There are two ways I could code this:
The first way is to create getters (i.e. houses, streets, suburbs) and select / deselect recipients that way.: messageRecipients.streets.select(aStreet).
Don't think I prefer this way but I don't want to see the Law of Demeter misapplied.
Some think this is all LoD has to say:
the Law of Demeter for functions requires that a method m of an object O may only invoke the methods of the following kinds of objects:
- O itself
- m's parameters
- Any objects created/instantiated within m
- O's direct component objects
- A global variable, accessible by O, in the scope of m
In particular, an object should avoid invoking methods of a member object returned by another method. For many modern object oriented languages that use a dot as field identifier, the law can be stated simply as "use only one dot". That is, the code a.b.Method() breaks the law where a.Method() does not. As an analogy, when one wants a dog to walk, one does not command the dog's legs to walk directly; instead one commands the dog which then commands its own legs.
Wikipedia: Law of Demeter
This is a monument to structural thinking. There is not a single semantic or conceptual thought in any of that. A static analyzer could enforce this. Bleh. This is not all I think of when I think of the LoD.
The Law of Demeter is not a dot counting exercise.
When you are an object, it is better to only talk to your friends, not friends of friends. If you randomly pull in anything you happen to need you'll soon find you've pulled together so many things that any change to the code base is going to hurt you right here.
Friends are things whose interfaces you own. Only then are you promised that you could jump through these dots and always find the same thing. While that can happen it's not going to be true if you just randomly grab whatever you like out of a code base.
So if the client calling "messageRecipients.streets.select(aStreet)" owns the interface for messageRecipients
and streets
then it's talking to its friends and there is no LoD violation. This means the client owns all the interfaces it uses. They should not change without the client's permission. This should be made clear to maintenance programmers. This needs clear boundaries. But it's not about dot counting.
That's the real problem. Because of the way you're getting this other object, the dependence on it's interface isn't explicit. The dependence can be a surprise. If you respect that you'll find some way to make this dependence clear to other coders.
This shouldn't be some cobbled together situation. This should be a situation created by careful design. What's being created here actually has a name. It's called a DSL. If that's what you're making fine. If this is just some randomly thrown together stuff because you happened to need it then you're creating a problem waiting to happen.
The other name the LoD goes by is "the principle of least knowledge". It's asking you to understand that when you create these dot chains you're adding knowledge to your client not only of these interfaces but how they connect. The more you know the more it hurts when things change. Don't create chains no one promised would be stable.
The other way would be to hide the fact that MessageRecipients is using SelectableEntity at all and simple create 'proxy' methods for each method on SelectableEntity (i.e. selectHouse, deselectHouse, selectStreet, etc).
I like this idea, but i'd also hide whether what's being selected is a house, street, or suburb. Let the suburb be the only thing that cares that it's a suburb.
Doesn't injecting @ConfigurationProperties classes violate the Law of Demeter. Specifically, doesn't it mask the real dependency (in this case an int) as described here?
Yes and yes. If OrderService
only requires an int
, then that's all you should be injecting. OrderProps
is a bag of properties that only exists as a convenient injection point for Spring, and you should never design your domain classes around a specific framework.
Littering code with @Value ... seems to have its own set of problems especially when refactoring / maintenance.
This is precisely why I'm not a fan of Spring component scan for larger projects. I find it cleaner and more maintainable to limit configuration and auto-wiring responsibilities to @Configuration
classes:
@Configuration
public class OrderServiceConfiguration {
@Autowired
private OrderProps orderProps;
@Bean
public OrderService orderService() {
return new OrderService(orderProps.getMaxItems());
}
}
Best Answer
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
, andMultiplesCalculationResult
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.