17

In his book API Design for C++, Martin Reddy elaborates on the Law of Demeter. In particular, he states that:

you should never call a function on an object that you obtained via another function call.

He supports his statement with a chaining function calls like

Func()
{
    [...]
    m_A.GetObjectB().DoSomething();
    [...]
}

Instead, he encourages to pass B as an argument to the function like:

Func(const ObjectB &B)
{
    [...]
    B.DoSomething();
    [...]
}

My question: why the latter example would produce more loosely coupled classes than the former one?

Korchkidu
  • 4,908
  • 8
  • 49
  • 69
  • Somewhat possibly related: http://programmers.stackexchange.com/q/203684/5409 – Richard J. Ross III Jul 05 '13 at 18:30
  • 1
    It looks like patent nonsense to me, a way to write convoluted and difficult-to-read code while ensuring job security because it becomes more difficult for others to maintain your code. – Dietrich Epp Jul 05 '13 at 18:41
  • 3
    I agree with @DietrichEpp that Demeter's law it is a bad idea. I won't deny that Demeters law enforces decoupling, which in turn can help to create maintainable code. But most of the time, I find decoupling not the only, nor the most effective way to create maintainable code. And making such a dogma out of it is even less helpful. –  Jul 05 '13 at 19:01
  • 7
    I wouldn't say the law is a bad idea, the bad idea is in trying to make it a law. They should've called it _Demeter's idea_ just to indicate that it's, as pretty much all concepts in programming, just a guideline of something that in some cases is a good thing to do. But not absolutely always. – stijn Jul 05 '13 at 19:33
  • Okay, so you don't trust A to play nice with B. But that means later that rather than A handling possible failure with B, you must now handle failures with A and B because you have a member A, and a function parameter B instead of just handling failures with member A. This is a terrible idea in my opinion. –  Jul 06 '13 at 02:02

5 Answers5

10

The analogy often used (including on the Wikipedia page, I notice) is that of asking a dog to walk — you'd ask the dog, you wouldn't ask for access to its legs and then ask its legs to walk.

Asking the dog to walk is a better decoupling because one day you might want a dog with something other than legs.

In your specific example, m_A's implementation may cease to depend on an instance of B.


EDIT: as some people want further exposition, let me try this:

If the object X contains the statement m_A.GetObjectB().DoSomething() then X must know:

  1. that m_A has an instance of the object B exposed via GetObject(); and
  2. that the object B has the method DoSomething().

So X needs to know the interfaces of A and B, and A must always be able to vend a B.

Conversely, if X merely had to do m_A.DoSomething() then all it needs to know is:

  1. that m_A has the method DoSomething().

The law therefore aids decoupling because X is now fully decoupled from B — it needs have no knowledge of that class whatsoever — and has less knowledge about A — it knows that A can achieve DoSomething() but it no longer needs to know whether it does that itself or whether it asks somebody else to do it.

In practise the law is often not used because it usually just means writing hundreds of wrapper functions like A::DoSomething() { m_B.DoSomething(); }, and the formal semantics of your program often explicitly dictate that A will have a B so you're not so much revealing implementation details by supplying GetObjectB() as you are merely fulfilling that object's contract with the system as a whole.

The first point can also be used to argue that the law increases coupling. Supposing you originally had m_A.GetObjectB().GetObjectC().GetObjectD().DoSomething() and you'd collapsed that down to m_A.DoSomething(). That means that because C knows that D implements DoSomething(), C must implement it. Then because B now knows that C implements DoSomething(), B must implement it. And so on. In the end you've got A having to implement DoSomething() because D does. So A ends up having to act in certain ways because D acts in certain ways whereas previously it might have had no knowledge of D whatsoever.

On the first point a comparable situation is Java methods traditionally declaring the exceptions they can throw. That means that they also have to list the exceptions that anything they call may throw if they don't catch it. So every time a leaf method adds another exception you have to walk up the call tree adding that exception to a whole bunch of lists. So a good decoupling idea ends up creating endless paperwork.

On the second point I think we stray into the 'is a' versus 'has a' debate. 'Has a' is a very natural way to express some object relationships and dogmatically hiding that behind a facade of "I have the locker keys so if you want your locker open just come and ask me, and I'll unlock it"-type conversations just obscures the task at hand.

Tommy
  • 99,986
  • 12
  • 185
  • 204
  • 12
    But, you wouldn't ask the library to read a book. You'd ask the library for the book, then read it yourself. Maybe tomorrow you want to buy the book instead, so you go to a bookstore, buy the book, and read it. For every style choice there is an analogy that supports it, which is why analogies make poor arguments for computer programming. – Dietrich Epp Jul 05 '13 at 18:45
  • In the second case, you need to be given the legs of the dog. It does not seem great either;) – Korchkidu Jul 05 '13 at 19:08
  • "In your specific example, m_A's implementation may cease to depend on an instance of B." If m_A has an instance of B, it is because it NEEDS it in the first place, not because Func would need A to call B (so I add it to A). – Korchkidu Jul 05 '13 at 19:11
  • 1
    @DietrichEpp that's why the Law of Demeter is not particularly well accepted; I nevertheless stand by my answer as an accurate answer to the question set. – Tommy Jul 05 '13 at 19:39
  • @Korchkidu if m_A has an instance of B then it needs one _in the current implementation_. It may not need one in the future. E.g. my car has a petrol tank because it needs one not because I bought it to act as a receptacle. But a future car may have no petrol tank. As there's no decoupling, if my car decides to power itself in a different way then I need to change how I refill it. If cars had agency like objects do then it would be a better design if I could just say 'okay, car, get fuel'. – Tommy Jul 05 '13 at 19:50
  • 1
    @Tommy: It's not necessarily the case that B is just part of the current implementation of A, but it might be part of the interface of A. If B is just part of the implementation, then it never should have been exposed in the first place. If it's part of the interface, then it's okay. For example, my car has passengers, not only because the passengers are necessary but because the passengers are a part of the car's interface. – Dietrich Epp Jul 05 '13 at 19:55
  • 1
    To expand, I can ask for the passengers to pay for gas. That's not part of the car's interface because the car is decoupled from money and payments. And I'm not going to take money out of my friend's wallet because that's not part of my friend's interface (maybe he keeps cash in his shoes, I don't care). – Dietrich Epp Jul 05 '13 at 19:57
  • 1
    @DietrichEpp but these things are often exposed exactly to avoid the wrapping and wrapping that the Law of Demeter implies. I think you're arguing against the pattern. I think this debate infringes on 'has a' versus 'is a'; with Law of Demeter you effectively pretend always to be in the 'is a' camp, exposing all composed functionality even when implemented internally as 'has a'. – Tommy Jul 05 '13 at 20:01
  • 2
    You're right, I am arguing against the pattern, because I think this answer is incomplete and one-sided. This is about shallow versus deep relationships in the interfaces between objects, and if you choose to make interfaces shallower then the interface complexity will increase. By comparison, the Wikipedia page has a more thorough explanation of the drawbacks of the Law of Demeter's approach and I recommend reading it. – Dietrich Epp Jul 05 '13 at 20:34
  • 1
    @DietrichEpp the question set is "why the latter example would produce more loosely coupled classes than the former one?" as per StackOverflow's focus on specific, focussed questions rather than discussions. As a result I didn't set out to provide an opinion on the law or even to attempt to nominate pros and cons objectively. Do you really think this answer incompletely answers that question? – Tommy Jul 05 '13 at 21:33
  • 1
    Let me rephrase things in that narrow context: `dog.getLegs().walk()` introduces coupling between the consumer of `dog` and how `walk()` is implemented, so we provide a `dog.walk()` interface. However, `library.readBook("Ulysses")` introduces coupling between `library` and `book`, so we expand it out to `library.find("Ulysses").read()`, so `library` doesn't need to know what you can do with a book and can just treat it as a generic object. This is the kind of pro/con I'm talking about, and why this answer about decoupling is incomplete. – Dietrich Epp Jul 05 '13 at 21:46
  • 1
    @DietrichEpp but library already knows about books because it has them. I'm of the opinion that the law always increases decoupling but is often unhelpful and in any case essentially unworkable in practise (and have updated my answer accordingly) due to non-decoupling concerns. Anyway, I've added detail to my answer since if you think it's necessary to answer the original question then it's necessarily relevant. – Tommy Jul 05 '13 at 22:14
  • @Tommy: your edit seems to mix m_a and B I think. Second example actually calls B.DoSomething(). The decoupling is about not using A::GetObjectB() I believe. – Korchkidu Jul 05 '13 at 22:30
5

The difference stands out a bit more when you look at unit tests.

Assume DoSomething() has a side-effect that you don't want to happen in your test code because it would be expensive or annoying to simulate, something like a database access or network communication for example.

In the first case in order to replace DoSomething() in your test you need to fake both ObjectA and ObjectB and inject the faked instance of ObjectA into the class containing Func().

In the second case you just call Func() with the fake ObjectB instance which greatly simplifies the test.

confusopoly
  • 1,245
  • 7
  • 19
4

To directly answer your question:

Version 2 produces more loosely coupled classes because Func in the first case depends on both the interface of the class of m_A and the class of the return type of GetObjectB (presumably ObjectB), while in the second case it only depends on the interface of class ObjectB.

That is, in first case, there's coupling between m_A's class and Func, in the second case, there isn't. If the interface of that class should ever change to not have GetObjectB(), but e.g. to have GetFirstObjectB() and GetSecondObjectB(), in the first case you'll have to rewrite Func to call the appropriate replacement function (and maybe even add some logic which one to call, maybe based on an additional function argument), while in the second version you can leave the function as it is and let the users of Func care about how to get that object of type ObjectB.

celtschk
  • 19,311
  • 3
  • 39
  • 64
  • Very clear explanation. Now I can clearly see the decoupling advantage of example 2 over example 1. Thanks! – Korchkidu Jul 05 '13 at 22:25
3

It is more flexible to changes. Imagine that m_A is an instance of an object A, developed by programmer Bob. If he decides to make a change in his code so that A no longer has a method to return an object of type B, then Alice, the developer of Func, would have to change her code too. Notice that you don't have this problem with the latter code snippet.

In software development this type of coupling results in what is called non-orthogonal designs, the kind of designs where you change a local part of the code somewhere and you need to change parts in other places as well.

Daniel Martín
  • 7,815
  • 1
  • 29
  • 34
  • Well, if when designing your API Bob decided to put an access to B from A, then it is clearly wrong to change the API after it is released. That's also the point of this book actually. Also, adding B as an argument add complexity to the API if Func is actually part of it. Moreover, I don't see why it would not be orthogonal. Could you elaborate on that point please? – Korchkidu Jul 05 '13 at 19:14
2

Well I think that it should be obvious why the chaining of functions together is bad as it produces longer harder to maintain code. In the top example Func() is an ugly function because it seems like it would just be called like

Func();

Essentially telling you nothing about the function. The second proposed method calls the function with a B passed to it, which not only makes it more readable but means that you can write a Func() for other classes without renaming it (since if it takes no parameters you can't rewrite it for another class). That tells you that Func() will do similar things to the object even if the class is different.

To answer the last part of your question the loose coupling is achieved because the first example implies that you have to get a B through A which couples the classes together, the second example is more general and implies that the B can come from anywhere.

aaronman
  • 18,343
  • 7
  • 63
  • 78
  • The counter part is that you API is less clean and require the client to do more "useless" job. – Korchkidu Jul 05 '13 at 19:17
  • Because it takes more arguments. The lesser, the better when designing an API. – Korchkidu Jul 05 '13 at 19:32
  • Totally disagree with that, a function should have the right amount of arguments, reducing argument count should be done by encapsulation not how it is done in your example – aaronman Jul 05 '13 at 19:36
  • Not to mention have you seen how many arguments some of the functions and methods in c++'s classes take – aaronman Jul 05 '13 at 19:37
  • More arguments, more methods, more classes as stated in the associated Wikipedia page. I have been taught to strive for minimal API during design... For the coupling issue, the only coupling removed I can see is not calling A::GetObjectB() method. However, if this method is in the API, it is because I deeply NEED it in the API. Not because somewhere, I needed to get a B from A. – Korchkidu Jul 05 '13 at 19:43
  • I'll agree that the decoupling is minimal, but if you don't understand the reasons that the second example is better code I don't think I can help you – aaronman Jul 05 '13 at 19:46
  • 2
    Actually from what we are given here, it is *impossible* to tell which one is the better code. Note that `m_A` hints at a member variable, which means that `m_A` is most likely a hidden implementation detail. Which makes it not unlikely that getting an `ObjectB` from it is also an implementation detail, and exposing that to the user of the class (so he can pass the `ObjectB` into the function) would therefore *increase* the coupling because now the clients would depend on an implementation detail of the class, namely that at some point an `ObjectB` is obtained and used. – celtschk Jul 05 '13 at 21:01
  • @aaronman: stating that one is simply better than the other is just plain wrong. At least, there are some drawbacks (stated here: http://en.wikipedia.org/wiki/Law_Of_Demeter#Disadvantages). Depending on what is important to you, I think that one solution could be better than the other. – Korchkidu Jul 05 '13 at 22:23
  • @Korchkidu the reason I am saying that the second example is almost assuredly better code is because the first results in a function call like `Func();`, calling a static function `Func` by itself taking no parameters is confusing. On the other hand if it has a parameter it is more obvious what is happening – aaronman Jul 06 '13 at 07:01
  • @celtschk are you saying in both examples there is coupling, the point I have been trying to get across is more that the first example is ugly code because of the chain of function calls – aaronman Jul 06 '13 at 07:05
  • 1
    I am saying that without context it is not possible to say which one is worse because the second version, while creating less immediate coupling, may well produce *worse* coupling in context if `Func` is part of the public interface, but `GetObjectB` is an implementation detail. Then the only way for clients to call the second version would be if the class exposes the implementation detail `GetObjectB` to the clients, which creates a coupling between all clients of that class and the implementation detail `ObjectB`, which is worse than the coupling introduced by the first version of `Func`. – celtschk Jul 06 '13 at 08:01