2

Suppose I have a Attendance class

public class Attendance {
   private PersonInfo personInfo;
   public PersonInfo getPersonInfo() {
      return personInfo;
   }
}

And I want to check if person is registered in the Conference class:

public class Conference {
   public boolean isRegistered(Attendance attendance) {
      PersonInfo personInfo = attendance.getPersonInfo(); 
      ...
   }
}

Because an Attendance object is passed to the method isRegistered, it should not violate the law of Demeter.

The method in Conference class returns a PersonInfo object, which is not an object that the Conference has direct access to. And there are not toString() and hashCode() methods in Conference class. From this aspect, does it violate the LoD?

If it does violate the LoD, can I replace the getter method with a field variable instead, i.e., attendance.personInfo?

lennon310
  • 3,242

4 Answers4

3

Absolutely yes, getters almost automatically violate the Law of Demeter.

Technically the LoD is not violated until you actually call the first method on the object you received from the getter, but that usually happens in the next line or so.

Having a public field instead of a getter does not change anything. The two are basically the same.

Also note, that there are ways to work around the LoD, like passing the object from the getter to another method immediately as parameter. These are obviously only workarounds and not solutions.

The whole message of LoD is that you should not access data, you should tell the object to do something with its data instead. This is in line with other principles like "Tell, don't Ask", Cohesion & Coupling, and others. The LoD will steer you in the direction of the right design, but you'll have to want to go that way too.

3

First of all, I want to say that I think I understand @RobertBräutigam when he says "yes, absolutely". I think, we could agree that once you open the door to a getter without a good reason, there's no excuse to close it to a second or more, what undoubtedly leads us the normalization of the deviation.

That said, I'm not so "absolutely" convinced that every single getter defeats LoD. LoD aims to reduce the knowledge component A has about the B' internals. But reducing doesn't mean remove completely. This is why the notion of "related" units or "friends". Friends use to know things about each other. There's going to be coupling, always, the key is where to place it so the software remains soft. Coupling becomes an issue when it happens in the wrong place or between the wrong components.

More formally, the Law of Demeter for functions requires that a method m of an object a may only invoke the methods of the following kinds of objects:[3]

a itself;

m's parameters;

any objects instantiated within m;

a's attributes;

global variables accessible by a in the scope of m

Wikipedia

The example with Attendance and Conference falls in the group m's parameters;

So given the example above, no, you are not breaking LoD until you do something like personInfo.doSomething(). If that's going to be the case of the immediate line of code we don't know, but it's likely to be the case.

can I replace the getter method with a field variable instead, i.e., attendance.personInfo?

No, for obvious reasons. LoD aims for high cohesion and low coupling, making internals accessible with no control at all is even worst than a wrong getter in the wrong place. The getter at least allows you to set different access modifiers to limit the visibility of the getter.

If you want to keep Conference and Attendance agnostic to each other, you need a layer of indirection or a new layer of abstraction. A place to solve att.getPersonInfo() and conference.isRegistered(somePerson).

The more decoupling you want, the more of these layers you need.

Laiv
  • 14,990
2

No. Using a getter doesn't guarantee that you're violating the Law of Demeter. AKA the Principle of Least Knowledge. As proof I'll finish your code example:

public class Conference {
   public boolean isRegistered(Attendance attendance) {
      PersonInfo personInfo = attendance.getPersonInfo(); 
      return (personInfo != null);
   }
}

Since this calls no methods on PersonInfo it doesn't violate even the strictest interpretation of LoD. LoD is not a prohibition against accessing data or using getters.

So hooray! You can keep using collections! (They use getters). I'm no fan of over use of getters but LoD is a poor argument against them. Argue real encapsulation instead. And no, public fields aren't any better. In C# they aren't even different.

But seriously you should be aware that LoD is warning you against doing some things that can cause subtle non-obvious problems.

However, there is a structural way of looking at LoD that I do not promote. Lavi is arguing it well but this way of enforcing LoD is as brainless as a Linter. I draw your attention to the very next wikipedia paragraph.

In particular, an object should avoid invoking methods of an 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.m().n() breaks the law where a.m() 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.

Wikipdedia - Law of Demeter - In Object Oriented Programming

Sorry but, "use only one dot", is just wrong. That isn't what this is about. The Law of Demeter Is Not A Dot Counting Exercise. This is about where you end up after that dot. Sorry but Wikipedia got this wrong. As proof I offer Java 8 streams:

int sum = widgets
    .stream()
    .filter(b -> b.getColor() == RED)
    .mapToInt(b -> b.getWeight())
    .sum()
;

I mean, that's a lot of dots. What gives? These aren't strangers is what. Each dot is taking you to classes that were meant to work together, were deployed together, and will only change together. These are all friends. Friends are ok. Random friends of friends are not.

LoD is telling you that if you just randomly walk a codebase that (oh my gosh) uses getters, there's a good chance you're going to stich together things that never promised you that they would only change together. That's what's so subtle about this. Your Linter doesn't know what's likely to change. So you don't get any meaningful warnings or errors until something changes. If you've been doing a lot of stitching, that can be bad.

There simply isn't a structural analysis that will judge this for you. You need to understand this. You need to know where the tectonic plates are in your codebase and stay way from the fault lines. Because someday, this stuff will move.

candied_orange
  • 119,268
0

You example is not complete to say.

If the next line of code is personInfo.doStuff() then yes you're breaking Demeter law.

if the newt line of code is this.makeSpeaker(personInfo) then no you're not breaking it.

In the first example you're talking to a nested object, whereas in the second one you're only talking to first level object and merely passing data around.

Just note that I'm only talking about Demeter Law, not good practices, my second example may not be the cleanest way to do things.

JayZ
  • 827