40

I understand the motive behind the principle of least knowledge, but I find some disadvantages if I try to apply it in my design.

One of the examples of this principle (actually how not to use it), which I found in the book Head First Design Patterns specify that it is wrong to call a method on objects that were returned from calling other methods, in terms of this principle.

But it seems that sometimes it is very needed to use such capability.

For example: I have several classes: video-capture class, encoder class, streamer class, and they all use some basic other class, VideoFrame, and since they interact with each other, they can do for example something like this:

streamer class code

...
frame = encoder->WaitEncoderFrame()
frame->DoOrGetSomething();
....

As you can see, this principle is not applied here. Can this principle be applied here, or is it that this principle cannot always be applied in a design like this?

4

4 Answers 4

25

The principle you are talking of (better known as Law of Demeter) for functions can be applied by adding another helper method to your streamer class like

  {
    frame = encoder->WaitEncoderFrame()
    DoOrGetSomethingForFrame(frame); 
    ...
  }

  void DoOrGetSomethingForFrame(Frame *frame)
  {
     frame->DoOrGetSomething();
  }  

Now, each function only "talks to friends", not to "friends of friends".

IMHO it is a rough guideline which can help to create methods which follow more strictly the single responsibility principle. In a simple case like the one above it is probably very opinionated if this is really worth the hassle and if the resulting code is really "cleaner", or if it will just expand your code formally without any noteable gain.

6
  • This approach does have the advantage of making it much easier to unit test, debug, and reason about your code.
    – gntskn
    Commented May 25, 2015 at 2:36
  • +1. Though, there is a very good reason not to make that code change is that the class interface may become bloated with methods whose job is merely make one or a few calls into several other methods (and without additional logic). In some types of programming environment, say C++ COM (notably WIC and DirectX), there is a high cost associated with the addition of each single method to a COM interface, compared to other languages.
    – rwong
    Commented May 25, 2015 at 2:52
  • 1
    In C++ COM interface design, decomposing large classes into small classes (which means you have a higher chance of having to talk to multiple objects) and minimizing the number of interface methods are two design goals with real benefits (cost reductions) based on deep understanding of the internal mechanics (virtual tables, code reusability, many other things). Therefore C++ COM programmers typically must ignore LoD.
    – rwong
    Commented May 25, 2015 at 2:55
  • 11
    +1: The Law of Demeter really should be named The Suggestion of Demeter: YMMV Commented May 27, 2015 at 7:44
  • 1
    Law of demeter is an advice for making architectural choices, while you suggest a cosmetic change so it seems you comply with that 'law'. That would be a misconception, because a syntactical change doesn't mean suddenly everything's all fine. As far as I know the law of demeter basically meant: if you want to do OOP, then stop writing prodecural code with getter functions everywhere. Commented Jun 2, 2018 at 20:52
49

The Principle of least knowledge or the Law of Demeter is a warning against entangling your class with details of other classes that traverse layer after layer. It tells you that it is better to talk only with your "friends" and not with "friends of friends".

Imagine you've been asked to weld a shield onto a statue of a knight in shiny plate armor. You carefully position the shield on the left arm so it looks natural. You notice there are three small places on the forearm, the elbow, and the upper arm where the shield happens to touch the armor. You weld all three places because you want to be sure the connection is strong. Now imagine your boss is mad because he can't move the elbow of his armor. You assumed the armor was never going to move and so created an immobile connection between forearm and upper arm. The shield should only connect to it's friend, forearm. Not to forearms friends. Even if you have to add a chunk of metal to make them touch.

Metaphors are nice but what do we actually mean by friend? Any thing an object knows how to create or find is a friend. Alternatively, an object can just ask to be handed other objects, of which it only knows the interface. These don't count as friends because no expectation on how to get them is imposed. If the object doesn't know where they came from because something else passed/injected it then it's not a friend of a friend, it's not even a friend. It's something the object only knows how to use. That's a good thing.

When trying to apply principles like this it's important to understand that they are never forbidding you from accomplishing something. They are a warning that you might be neglecting to do more work to achieve a better design that accomplishes the same thing.

No one wants to do work for no reason so it's important to understand what you're getting out of following this. In this case it keeps your code flexible. You can make changes and have fewer other classes impacted by the changes to worry about. That sounds good but doesn't help you decide what to do unless you take it as some kind of religious doctrine.

Instead of blindly following this principle take a simple version of this problem. Write a solution that doesn't follow the principle and one that does. Now that you have two solutions you can compare how receptive each is to changes by trying to make them in both.

If you CAN'T solve a problem while following this principle there is likely another skill you're missing.

One solution to your particular problem is to inject the frame into something (a class or method) that knows how to talk to frames so you don't have to spread all those frame chatting details in your class, which now only knows how and when to get a frame.

That actually follows another principle: Separate use from construction.

frame = encoder->WaitEncoderFrame()

By using this code you've taken responsibility for somehow acquiring a Frame. You haven't yet assumed any responsibility for talking to a Frame.

frame->DoOrGetSomething(); 

Now you have to know how to talk to a Frame, but replace that with:

new FrameHandler(frame)->DoOrGetSomething();

And now you only have to know how to talk to your friend, FrameHandler.

There are many ways to achieve this and this is, perhaps, not the best one but it shows how following the principle doesn't make the problem unsolvable. It's just demanding more work from you.

Every good rule has an exception. Best examples I know are internal Domain Specific Languages. A DSLs method chain seems to abuse the Law of Demeter all the time because you are constantly calling methods that return different types and using them directly. Why is this OK? Because in a DSL everything that is returned is a carefully designed friend that you are meant to talk to directly. By design, you have been given the right to expect a DSL's method chain not to change. You don't have that right if you just randomly delve into the code base chaining together whatever you find. The best DSLs are very thin representations or interfaces to other objects that you likely shouldn't delve into either. I only mention this because I found I understood the law of demeter much better once I learned why DSLs are a good design. Some go so far as to say DSL's don't even violate the real law of demeter. They just seem to when you look at the law superficially.

Another solution is to let something else inject frame into you. If frame came in from a setter or preferably a constructor then you're not taking any responsibility for constructing or acquiring a frame. This means your role here is much more like FrameHandlers was going to be. Instead now you're the one who's chatty with Frame and making something else figure out how to get a Frame In a way this is the same solution with a simple change of perspective.

The SOLID principles are the big ones I try to follow. Two being respected here are Single Responsibility and Dependency Inversion Principles. It's really hard to respect these two and still end up violating the Law of Demeter.

The mentality that goes into violating Demeter is like eating at a buffet restaurant where you just grab whatever you want. With a little work upfront you can provide yourself with a menu and a server who'll bring you anything you like. Sit back, relax, and tip well.

3
  • 2
    "It tells you that it is better to talk only with your "friends" and not with "friends of friends"" ++
    – RubberDuck
    Commented May 17, 2015 at 18:43
  • 1
    The second paragraph, is that stolen from somewhere, or did you make it up? That made me understand the concept entirely. It made it blatant to what "friends of friends" are, and what drawbacks there are to entangling too many classes (joints). A+ quote.
    – mausworks
    Commented Jan 23, 2016 at 19:19
  • 4
    @diemaus If I stole that shield paragraph from somewhere it's source has since leaked out of my head. At the time my brain just thought it was being clever. What I do remember is that I added it after much of the upvotes so it's nice to see it validated. Glad it helped. Commented Jan 23, 2016 at 23:58
22

Is functional design better than object-oriented design? It depends.

Is MVVM better than MVC? It depends.

Amos & Andy or Martin and Lewis? It depends.

What does it depend on? The choices you make depend on how well each technique or technology meets your software's functional and non-functional requirements, while adequately satisfying your design, performance and maintainability goals.

[Some book] says that [some thing] is wrong.

When you read this in a book or blog, evaluate the claim based on its merit; that is, ask why. There is no right or wrong technique in software development, there is only "how well does this technique meet my goals? Is it effective or ineffective? Does it solve one problem but create a new one? Can it be well-understood by the entire development team, or is it too obscure?"

In this particular case -- the act of calling a method on an object returned by another method -- since there is an actual design pattern that codifies this practice (Factory), it's difficult to imagine how one could make the assertion that it is categorically wrong.

The reason it's called the "Principle of Least Knowledge" is that "Low Coupling" is a desirable quality of a system. Objects that are not tightly-bound to each other work more independently, and are therefore easier to maintain and modify individually. But as your example shows, there are times when high-coupling is more desirable, so that objects can more effectively coordinate their efforts.

2

Doc Brown's answer shows a classic textbook implementation of Law of Demeter - and the annoyance/disorganized-code-bloat of adding dozens of methods that way is probably why programmers, myself included, often don't bother doing so, even if they should.

There is an alternative way to decouple the hierarchy of objects:

Expose interface types, rather than class types, via your methods and properties.

In Original Poster's (OP's) case, encoder->WaitEncoderFrame() would return an IEncoderFrame instead of a Frame, and would define what operations are permissible.


SOLUTION 1

In the easiest case, Frame and Encoder classes are both under your control, IEncoderFrame is a subset of methods Frame already publicly exposes, and Encoder class doesn't actually care what you do to that object. Then, implementation is trivial (code in c#):

interface IEncoderFrame {
    void DoOrGetSomething();
}

class Frame : IEncoderFrame {
    // A method that already exists in Frame.
    public void DoOrGetSomething() { ... }
}

class Encoder {
    private Frame _frame;
    public IEncoderFrame TheFrame { get { return _frame; } }
    ...
}

SOLUTION 2

In an intermediate case, where Frame definition is not under your control, or it would not be appropriate to add IEncoderFrame's methods to Frame, then a good solution is an Adapter. That is what CandiedOrange's answer discusses, as new FrameHandler( frame ). IMPORTANT: If you do this, it is more flexible if you expose it as an interface, not as a class. Encoder has to know about class FrameHandler, but clients only need to know interface IFrameHandler. Or as I named it, interface IEncoderFrame - to indicate that it is specifically Frame as seen from POV of Encoder:

interface IEncoderFrame {
    void DoOrGetSomething();
}

// Adapter pattern. Appropriate if no access needed to Encoder.
class EncoderFrameWrapper : IEncoderFrame {
    Frame _frame;
    public EncoderFrameWrapper( Frame frame ) {
        _frame = frame;
    }
    public void DoOrGetSomething() {
        _frame....;
    }
}

class Encoder {
    private Frame _frame;

    // Adapter pattern. Appropriate if no access needed to Encoder.
    public IEncoderFrame TheFrame { get { return new EncoderFrameWrapper( _frame ); } }

    ...
}

COST: Allocation and GC of a new object, the EncoderFrameWrapper, every time encoder.TheFrame is called. (You could cache that wrapper, but that adds more code. And is only easy to code reliably if encoder's frame field cannot be replaced with a new frame.)


SOLUTION 3

In the more difficult case, the new wrapper would need to know about both Encoder and Frame. That object would itself violate LoD - it is manipulating a relationship between Encoder and Frame that should be Encoder's responsibility - and probably be a pain to get right. Here is what can happen if you start down that road:

interface IEncoderFrame {
    void DoOrGetSomething();
}

// *** You will end up regretting this. See next code snippet instead ***
class EncoderFrameWrapper : IEncoderFrame {
    Encoder _owner;
    Frame _frame;
    public EncoderFrameWrapper( Encoder owner, Frame frame ) {
        _owner = owner;   _frame = frame;
    }
    public void DoOrGetSomething() {
        _frame.DoOrGetSomething();
        // Hmm, maybe this wrapper class should be nested inside Encoder...
        _owner... some work inside owner; maybe should be owner-internal details ...
    }
}

class Encoder {
    private Frame _frame;

    ...
}

That got ugly. There is a less-convoluted implementation, when the wrapper needs to touch details of its creator/owner (Encoder):

interface IEncoderFrame {
    void DoOrGetSomething();
}

class Encoder : IEncoderFrame {
    private Frame _frame;

    // HA! Client gets to think of this as "the frame object",
    // but its really me, intercepting it.
    public IEncoderFrame TheFrame { get { return this; } }

    // This is the method that the LoD approach suggests writing,
    // except that we are exposing it only when the instance is accessed as an IEncoderFrame,
    // to avoid extending Encoder's already large API surface.
    public void IEncoderFrame.DoOrGetSomething() {
        _frame.DoOrGetSomething();
       ... make some change within current Encoder instance ...
    }
    ...
}

Granted, if I knew I would end up here, I might not do this. Could just write the LoD methods, and be done with it. No need to define an interface. On the other hand, I like it that the interface wraps related methods together. I like how it feels to do the "frame-like operations" to what feels like a frame.


FINAL COMMENTS

Consider this: If the implementor of Encoder felt that exposing Frame frame was appropriate to their overall architecture, or was "so much easier than implementing LoD", then it would have been much safer if they instead did the first snippet I show - expose a limited subset of Frame, as an interface. In my experience, that is often a completely workable solution. Just add methods to the interface as needed. (I am talking about a scenario where we "know" Frame already has the needed methods, or they would be easy and non-controversial to add. The "implementation" work for each method is adding one line to the interface definition.) And know that even in the worst future scenario, it is possible to keep that API working - here, by removing IEncoderFrame from Frame and putting it on Encoder.

Also note that if you don't have permission to add IEncoderFrame to Frame, or the needed methods don't fit well to the general Frame class, and solution #2 doesn't suit you, perhaps because of the extra object creation-and-destruction, solution #3 can be seen as simply a way to organize the methods of Encoder, to accomplish LoD. Don't just pass through dozens of methods. Wrap them in an Interface, and use "explicit interface implementation" (if you are in c#), so that they can only be accessed when the object is viewed through that interface.

Another point I want to emphasize is that the decision to expose functionality as an interface, handled all 3 of the situations described above. In the first, IEncoderFrame is simply a subset of Frame's functionality. In the second, IEncoderFrame is an adapter. In the third, IEncoderFrame is a partition into Encoders functionality. It doesn't matter if your needs change between these three situations: the API stays the same.

4
  • Coupling your class to an interface returned by one of its collaborators, rather than a concrete class, while an improvement, is still a source of coupling. If the interface needs to change, it means your class will need to change. The important point to take away is that coupling is not inherently bad as long as you make sure you avoid unnecessary couple to unstable objects or internal structures. This is why the Law of Demeter needs to be taken with a large pinch of salt; it instructs you to always avoid something that may or may not be a problem, depending on the circumstances. Commented Feb 4, 2017 at 23:38
  • @PeriataBreatta - I certainly can't and don't disagree with that. But I'd like to point out one thing: An Interface, by definition, represents what needs to be known at the boundary between the two classes. If it "needs to change", that is fundamental - no alternative approach could have somehow magically avoided the needed coding. Contrast that with doing any of the three situations I describe, but not using an interface- instead,return a concrete class. In 1, Frame, in 2, EncoderFrameWrapper, in 3, Encoder. You lock yourself into that approach. The interface can adapt to them all. Commented Feb 7, 2017 at 19:33
  • @PeriataBreatta ... which demonstrates the benefit of explicitly defining it as an interface. I hope eventually to enhance an IDE to make that so convenient, that interfaces will be used much more heavily. Most multi-level accesses would be via some interface, hence be much easier to manage changes. (If that is "overkill" for some cases, code analysis, combined with annotations about where we are willing to take the risk of not having an interface, in exchange for the small performance boost, could "compile it out' - replacing it with one of those 3 concrete classes from the 3 "solutions".) Commented Feb 7, 2017 at 19:41
  • @PeriataBreatta - and when I say "the interface can adapt to them all", I'm not saying "ahhhh, its wonderful that this one language feature can cover these different cases". I'm saying that defining interfaces minimizes the changes you might need to make. In the best case, the design can change all the way from the simplest case (solution 1), to the most difficult case (solution 3), without the interface changing at all - only the producer's internal needs have become more complex. And even when changes are needed, they tend to be less pervasive, IMHO. Commented Feb 7, 2017 at 19:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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