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?

9
  • 3
    It boggles the mind why you would think that accessing the data directly would be better than getting it using an accessor.
    – gnasher729
    Jan 21, 2021 at 8:11
  • @gnasher729 because getters have been so reviled that new developers look at them as forbidden or bad practice. So if I don't write getter, I won't go hell at the of this so confusing existence.
    – Laiv
    Jan 21, 2021 at 9:58
  • @gnasher729: "I should wrap this just to be safe" is a thought process that's prone to infinite recursion. I've seen countless examples of developers who take their "one more abstraction" approach to insane levels and end up abstracting for abstraction's sake. Questioning whether an additional abstraction is warranted and meaningfully contributes is not madness, so it should leave your mind rather unboggled.
    – Flater
    Jan 21, 2021 at 10:21
  • @Laiv I think it is a three-strike system. You might not go to hell for the first getter, but I think you definitely should by the third or so. :) Seriously though getters are the most clear and reliable sign of a defective design. The more there are, the worse the design usually is. Jan 21, 2021 at 11:22
  • 1
    LoD basically tries to guide you away from writing procedural code. Consider why you want the personInfo object, and why you want to return it from isRegistered, and can you make attendance do that work instead. Perhaps you'd have to preconfigure the attendance object somehow in order to achieve that. Perhaps you'd have to reconceptualize and redesign things that interact with that class. That said, even in an OO system, there can be procedural bits. Sometimes an object can just be a data bag. 2/2 Jan 22, 2021 at 13:43

4 Answers 4

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.

2
  • Thank you. If I want to check whether a given attendance registered for the conference or now, how should I refactor my second class? Should I have a mid-layer class instead? Jan 21, 2021 at 19:17
  • @user3153970 It is difficult to help without exact context, but I would not have more classes/objects. Instead, try to model your problem with the existing objects better. If you want to go down the path of proper oo and avoid LoD violations your design will change. It will be completely different from how you think now. Your first class is already broken, and some code will violate LoD because of that. Changing the second class only will not solve this problem. Jan 21, 2021 at 19:56
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.

2
  • Thank you. So if I have a new layer, will the constructor include both Conference and Attendance instance? And will this not violate the LoD if I still need person info to call some method in order to check? Jan 21, 2021 at 19:19
  • It should not, but if you goes further than infoPerson, then yes, you could be breaking LoD.
    – Laiv
    Jan 21, 2021 at 21:29
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.

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.

1
  • In the second case, surely it depends on what makeSpeaker does! Jan 21, 2021 at 11:42

Not the answer you're looking for? Browse other questions tagged or ask your own question.