24 June 2004

Law of Demeter

I am curently reading the book The Pragmatic Programmer, which was recomanded on Eric Gunnerson blog. I have just read a section about The Law of Demeter and realised that I often break it. This law says that a class method should only call methods of: (1) this object, (2) objects passed in as parameters, and (3) objects locally created (including on the stack). This means that code like obj.f().g() or A* objA = objB.GetA(); objA->DoIt() are breaking the law. The authors suggest two possible solutions.
  1. [look at first example] If obj is passed in as a parameter then it might be possible to simply pass in obj.f() and simply call g(). This might not be possible in some circumstances, for example if you also need obj.h(). In these cases you...
  2. [look at second example] Write a wrapper function DoIt() in the class of objB that simply forwards the request to objA. This way you can write simply objB.DoIt().
Why bother? Well, from a theoretical point of view you minimize the coupling between modules. Why? Well, let's look at the second example. The class of objB looks like this:
class B {
   // ...
public:
   A* GetA() const;
};
In other words it is already coupled to class A since it contains a function that returns a pointer to A. Adding a wrapper DoIt method does NOT increase the number of classes on which class B depends.
// B.hpp
class B {
   // ...
public:
   A* GetA() const;
   void DoIt() const;
};

// B.cpp
void B::DoIt() const
{
   GetA()->DoIt();
}
However writing such a wrapper will decrease the number of classes on which the client of class B depends. In the original version it depended on class B and class A. With the DoIt wrapper in place it depends only on class B. But writing such wrappers is cumbersome and boring. Does it really pay off? I think that it is a situation very similar to writing properties (C#) or acessors (Java/C++) and accessing members thru them. If you found situations in the past when you said "Why didn't I used an accessor here?" then you are likely to find the Law of Demeter useful. Avoiding call chains like obj.f()->g()[3].h() makes the code more readable. It also brings down compilation times. I wish there were some tools to help with this by automatically doing the refactoring illustrated above.

1 comment:

rgrig said...

I guess the short tip is: "Keep includes/imports at a minimum."

Post a Comment

Note: (1) You need to have third-party cookies enabled in order to comment on Blogger. (2) Better to copy your comment before hitting publish/preview. Blogger sometimes eats comments on the first try, but the second works. Crazy Blogger.