24

Any time I see articles about Law of Demeter the author never seems to give a solid example of how to obey this law. They all explain what it is and show an example of breaking the law but that is easy.

There is probably lots of ways to obey this law (good design and planning being one) but in very simple terms would this be a way to obey it?

Let's say I have a class with these properties:

public class Band {

    private Singer singer;
    private Drummer drummer;
    private Guitarist guitarist;
}

I am somewhere in the program and I have an instance of this Band class and I want the guitarists name, what I usually see is something like:

guitaristName = band.getGuitarist().getName();

That one does not seem too bad as it is not going too deep in the chain but is Law of Demeter saying that maybe it should be done this way:

guitaristName = band.getGuitaristName();

and my Band class has a method:

public String getGuitaristName() {
    return guitarist.getName();
}

Is this how you are supposed to obey the law?

Thanks.

ibanore
  • 1,500
  • 1
  • 12
  • 25
  • 2
    Some would say that adding a `getGuitaristName` method to `Band` would be violating the Law of Demeter: you've forced `Band` to have knowledge of the structure of `Guitarist`. That creates a design dependency where none might have existed before. It's all a question of perspective, I think. – Ted Hopp Jun 30 '13 at 17:52
  • 8
    @TedHopp: Those people would be wrong. The LoD does not prevent `Band` from sending requests to `Guitarists` since it already knows about that. It simply prevents users of `Band` from accessing `Guitarists` directly, since users of `Band` shouldn't know about its internals – Dancrumb Jun 30 '13 at 17:56
  • @Dancrumb So would you say what I did above with the `getGuitaristName()` method is a valid way to obey this law? At least if the object graphs are not too large like above. – ibanore Jun 30 '13 at 17:58
  • 2
    @Dancrumb - This is true; in fact, the proliferation of such method in classes like `Band` is [cited as one of the disadvantages of the LoD](http://en.wikipedia.org/wiki/Law_of_Demeter#Disadvantages). My view, however, is that while such refactoring conforms to the letter of the Law, it is in direct violation of the spirit of the Law. (Why should a `Band` object need to know that a `Guitarist` has a `String`-valued name attribute?) From a design perspective, the structure of a `Guitarist` is not really part of the "deep structure" of a `Band`. – Ted Hopp Jun 30 '13 at 18:01
  • 2
    Citing Wikipedia: "a given object should assume as little as possible about the structure or properties of anything else (*including its subcomponents*)"---emphasis mine. – Marko Topolnik Jun 30 '13 at 18:02
  • @TedHopp - I'm inclined to agree with you on this method (and others like it), where you've essentially 'encoded' the internal object type in the method name. It's a cheat around the strict definition of LoD – Dancrumb Jun 30 '13 at 18:07
  • @MarkoTopolnik So that means having a `getGuitaristName` method is not the best choice but that then means I have to do `getGuitarist.getName()`. I know that the chain is not too deep but what if I wanted his guitar make so that would be `band.getGuitarist().getGuitar().getMake()`. I really don't like the look of that, it seems too long. – ibanore Jun 30 '13 at 18:08
  • 1
    David, better read my answer. This was a side note, assuming the premise that the entire conversation is meaningful, which I think it isn't. – Marko Topolnik Jun 30 '13 at 18:10
  • 2
    @David - In the case of `band.getGuitarist().getGuitar().getMake()`, the issue is whether the make of guitar (and the guitar itself) is strictly a property of the guitarist or whether it is a property of "the guitarist in this band". In my view, it is more reasonable to widen the `Band` interface in the latter case than in the former. – Ted Hopp Jun 30 '13 at 18:22

4 Answers4

45

I think the idea of the law is, as Dancrumb says above, to ensure that people are accessing objects at the appropriate level.

Imagine that our Band class models the front desk of the band's offices. It's job is to act as a PA to the band members & deal with anyone who wants to interact with the band.

Now let's say we had a tour promoter from a PR company who wants to put together some PR material for their next tour. We could model him with a class:

class TourPromoter {

  public String makePosterText(Band band) {
    String guitaristsName =  band.getGuitarist().getName();
    String drummersName = band.getDrummer().getName();
    String singersName = band.getSinger().getName();
    StringBuilder posterText = new StringBuilder();

    posterText.append(band.getName()
    posterText.append(" featuring: ");
    posterText.append(guitaristsName);
    posterText.append(", ");
    posterText.append(singersName);
    posterText.append(", ")
    posterText.append(drummersName);
    posterText.append(", ")
    posterText.append("Tickets £50.");

    return posterText.toString();
  }

}

In real life, this is the equivalent of the tour promoter ringing up the office & saying:

  • Tour Promoter: Can I speak to your guitarist?

  • Receptionist: OK, I'll get him for you.

  • Guitarist: Hello, this is the guitarist

  • Tour Promter: Hi, I'm putting together your latest poster. Just wanted to check your name?

  • Guitarist: It's Jimmy Page

  • Tour Promoter: Great, thanks. Oh, could you get me your drummer?…

Suffice it to say that the PA would get fired pretty quickly. Most reasonable people would ask "Couldn't you have handled that phone call for us?"

We could have a getGuitaristsName() method, which would technically be honouring the Law of Demeter, but we're still asking our TourPromoter class to remember details about the band — i.e. that they have a guitarist — whereas this information should really belong to the band itself.

To make sure we're introducing methods in a sensible way, we need to look at what the tour promoter is actually looking for — i.e. the names of all the band members. If we model that method in code, it gives us greater flexibility to make changes to the Band later on, without having to even touch TourPromoter:

public class Band {
    private Singer singer;
    private Drummer drummer;
    private Guitarist guitarist;

    public String[] getMembers() {
        return {singer.getName(), drummer.getName(), guitarist.getName()};
    }  
}

public class TourPromoter {
    public String makePosterText(Band band) {
        StringBuilder posterText = new StringBuilder();

        posterText.append(band.getName());
        posterText.append(" featuring: "); 
        for(String member: band.getMembers()) {
            posterText.append(member);
            posterText.append(", ");
        }
        posterText.append("Tickets: £50");

        return posterText.toString();
    }    
}

If we now add a Bassist or KeyboardPlayer, only the Band class needs to know the difference & the Tour Promoter doesn't need to change. This means that we're now also honouring the Single Responsibility Principle too — i.e. our makePosterText() method only needs to change if we change our poster format, not if the band changes.

I don't think the Law of Demeter will tell you which method you need to pull out in order to meet the principle in the best way (e.g. getMembers() rather than getGuitaristsName() above) & in that way, I think you are right — it does show you when things are broken, but not necessarily how to fix them. Having the LoD in mind though, means that you keep an eye out for violations that can then be fixed through a combination of other design principles, like SRP.

anotherdave
  • 6,656
  • 4
  • 34
  • 65
  • 9
    Great answer. In a real example I would have a method `getBandMemberInfo` which returns a `Collection` where `BandMemberInfo` is an interface implemented by Singer, Drummer and Guitarist which has getters (but no setters) for name, instrument, picture, biography and everything else needed to create info material about the band. But I think your example already explains it pretty well. – Philipp Jul 07 '13 at 23:27
  • This is really one of the most easy to understand explanations provided to understand the Law of Demeter. Great answer – Prahalad Deshpande Jun 17 '14 at 16:29
  • 1
    Nice zep reference – Lawrence Tierney Oct 25 '17 at 14:22
29

You are not applying LoD at the appropriate level: both Band and Guitarist should be considered a part of the same module and the dilemma you have shall be decided on the grounds of maximum convenience.

Your question is an example of a much wider problem, which I have frequently met in books on design patterns and similar: they try to explain wide-reaching principles, which concern the design of a complex system, on ridiculously undersized problems. The result is just reader's confusion.

Where you'd actually see this principle in effect is something like this: you are using AsyncHttpClient, which is an abstraction built atop Netty, which is an abstraction built atop Java NIO. Now, if AsyncHttpClient's API forced you at some place to directly manage a Java NIO object, whose API is much more raw, and deals with concepts completely foreign to AsyncHttpClient, that would be an example of breaking LoD.

Marko Topolnik
  • 195,646
  • 29
  • 319
  • 436
5

The Law of Demeter itself doesn't present a methodology for good demand. At best, it's a useful heuristic for identifying potentially problematic areas of code. Deviations from the law can be considered a 'code smell'

It can have the tendency to widen the interfaces of your classes, since it results in proxy, or wrapper, methods to give you access to properties that are owned by internal objects. As a result, its advantages should be balanced against its disadvantages (of potentially creating unwieldy classes and module).

It is best applied as a tool to identify code that may need refactoring. However, a simple swap of methods from

a.x.getFoo()

to

a.getXFoo()

is following the letter of the law, without following the spirit (acknowledgements to @TedHopp for that point). Instead, you should be looking into what users are trying to do when they pierce a's abstraction in order to get x's foo and define that method accordingly. It may not be a trivial task, but that's why we get paid the big bucks :)

Dancrumb
  • 26,597
  • 10
  • 74
  • 130
2

I'd have a Band answer the question who's playing this instrument?

public String whoIsPlaying(String instrument);

Then if you need a guitarist's name you'd say:

band.whoIsPlaying("guitar");

Nothing stops you from pinging other instruments as well.

Weltschmerz
  • 2,166
  • 1
  • 15
  • 18