Let's start with Wikipedia:
More formally, 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
Rule 1:
public class ClassOne {
public void method1() {
method2();
}
public void method2() {
}
}
Rule 2:
public class ClassOne {
public void method1(ClassTwo classTwo) {
classTwo.method2();
}
}
class ClassTwo {
public void method2() {
}
}
Rule 3:
public class ClassOne {
public void method1() {
ClassTwo classTwo = new ClassTwo();
classTwo.method2();
}
}
class ClassTwo {
public void method2() {
}
}
Rule 4 (thanks @juharr):
public class ClassOne {
private ClassTwo classTwo;
public void method1() {
classTwo = new ClassTwo();
classTwo.method2();
}
}
class ClassTwo {
public void method2() {
}
}
Rule 5:
?
Can anyone help me with Rule 5?
And doesn't Law of Demeter imply that chaining is bad?
User.getName().getLastName();
This leads to high coupling.
Isn't "Tell, don't ask" a similar principle?
So is this everything? Am I wrong about something? How can you obey Law of Demeter?
"Tell don't ask" is a bit different.
Demeter: don't get something to get something from that to do something on the final thing.
TDA: don't retrieve "information" from another object to then make a decision on that. Simple example:
if (someList.size() == 0) { bla
vs.
if (someList.isEmpty()) { bla
In both cases you are calling a method on some other object; but there is a key difference: the first call exposes "internal" state of that other object to you; on which you then make some decision. Whereas, in the "TDA" improved second version; you leave that "status evaluation" within that other object; thereby somehow reducing coupling.
But just for the record: that second example still makes a decision based on the state of that list. From that point of view, it is just a slightly better version than option 1. Ideally, you wouldn't need such checks.
The 5th is difficult to represent in C# or Java, since they don't technically support global variables. However, in a design pattern that is similar in principle, you could have e.g. a configuration class that just contains globally-accessible static configuration values, such as (C#):
internal class MyConfiguration
{
private static String MyConfigurationValue; // set in constructor
MyConfiguration(){ MyConfigurationValue = DoSomethingToLoadValue(); }
public static String GetMyConfigurationValue(){ return MyConfigurationValue; }
}
In this case (assuming the design pattern was acceptable in all other ways), the Law of Demeter would allow this, since it is globally accessible and intended to be that way.
An example for Rule 5 would be:
public class ClassOne {
public void method1() {
classTwo.STATIC_INSTANCE.method2();
}
}
class ClassTwo {
public static final ClassTwo STATIC_INSTANCE = ...;
public void method2() {
}
}
Enums basically work this way, and it's ok to access enums.
Your example:
user.getName().getLastName();
obviously contradicts the laws, since the object you get from "getName()" will not fall into any of the categories listed. Note: this is wrong even if you are not using chain-calls:
Name name = user.getName();
name.getLastName(); // <- this is still wrong
since the object "name" still does not fall into any of the listed categories.
However things like this are ok:
reportBuilder.withMargin(5).withFooter(10)
.withBorder(Color.black).build();
Why is this allowed? Because you either get the same object (the reportBuilder) back each time, or maybe a new object each time if the builder is implemented as immutable. Either way, it falls into law 2 or 3, so it's ok either way.
Your third question is "how to obey". Well, this is a complex question, but just to start, think about what kind of methods are actually forbidden by the laws!
Just put the laws into negative: We shouldn't call methods on objects that are already there (because new objects are exempt), and are not my object, or fields of my object, or my parameters. So that leaves objects that are in the fields of other objects!
So basically that means you shouldn't be able to "get" access to objects that are not you, not in your fields, and not direct parameters. Which I would summarize as "no getters"!