Demeter’s is one of the great ideals of good coding practice in my opinion.
You’ll find few guidelines that connect or touch upon so many other core principles of quality programming. The law of Demeter involves modularity, encapsulation, cohesion, coupling, responsibility stratification, and information hiding. If you want to DRY and KISS, then you’ll have use for Demeter.
In short, Demeter’s Law says, “Only talk to your neighbors [as concerns objects]”. I like to say it as, “Don’t you dare access the insides of an object from the outside!”. This pertains to reaching inside an object and using its internal parts, rather than using a more limited public interface as provided by said object. Now, before I show some code examples of how this shakes out, I’d like to illustrate the general idea via a metaphor first …
Imagine that you are baking up something sweet and find that you need sugar quickly. You know that your next door neighbor has sugar. So you head over there to get some. The law of Demeter is the difference between knocking on the door to ask for the sugar, and going into your neighbor’s house yourself and just taking some. That’s it in a nutshell. The implications are amusing and interesting I think.
Aside from the illegality and astounding rudeness, there are all kinds of problems with going in and taking the sugar yourself. Let’s assume that the neighbor is hugely generous and will allow you to enter and take sugar whenever you want; right from their cupboard. This is essentially the difference between public and protected / private. Anyway, continuing on, let’s assume that this emergency sugar problem is a regular occurrence and that the sugar is a public resource (variable).
Now, say I’m the neighbor here. Even though I’m amazingly trusting of you, allowing unrestricted access to my sugar stockpile, there’s all kinds of problems this relationship might cause for me. Since your sugar requisitioning is unsupervised, I don’t know what you are doing. What if the sugar runs out and I don’t notice? What if you move stuff around while looking for the sugar? What if you abuse my trust and take something else? Very quickly, anything that’s mysteriously out of place in my kitchen or house might be blamed on you. However, without detailed case-by-case troubleshooting, I’ll never know which phenomena to blame on you and which not to. This makes fixing problems a lot more complicated for me! What if you aren’t the only person I allow to take my sugar? Now everyone is stepping on each others toes, it can all be a huge mess. Good luck sorting it out!
These problems are not all one-sided either. Such a relationship can also cause you great trouble. In this case, being dependent upon me for sugar, you are susceptible to changes in the sugar stash. What if I move the sugar and you can’t find it? What if I run out and forget to buy more? What if I replace my white cane sugar with indistinguishable Splenda and you ruin a recipe because of it? What if I become a sugar collector and suddenly have 127 different kinds of sugar in my house and you don’t know which one to use? I could go on for a long time making up these kinds of problems. The point is that many of these problems would be averted or lessened if you just asked me for the sugar at the door.
Now, if we make the sugar a protected resource, like putting a lock on it, then you have to ask me for sugar each time. At first, this might feel like I’m withdrawing my generosity and being all passive-aggressive about giving out sugar suddenly. However, as you see from the above paragraphs, this solves many gnarly problems. A little control imposed by me increases the quality of our cooperation and allows me to more easily coordinate with a greater number of people. Should sugar rationing suddenly start for example, I’m in a much better position to ensure the fair distribution of my sugar to all who depend upon me for it.
Now, let’s look at some code! I’m going to use php 5 since that’s what I know best.
A sample object:
class ObjectLister{
public $list_of_objects; //an array of objects managed by this class.
public function addObject($object_to_add){};
public function getObject($identifier){};
}//end class
class RandomObject{
protected $value;
public function important_output_function();
}//end class
So these are simple classes, but not fully implemented here. Code walls make eyes glaze over, so let’s just pretend that addObject(), getObject(), and important_output_function() are implemented fully. How they actually work isn’t important for our discussion today.
So, maybe I have a cart that needs to use the ObjectLister class. Here’s a not so great way of doing so:
$Cart->SomeList = new ObjectLister();
$Cart->SomeList->list_of_objects[1] = new RandomObject(1);
$Cart->SomeList->list_of_objects[2] = new RandomObject(2);
$Cart->SomeList->list_of_objects[3] = new RandomObject(3);
///1..2..skip a few….
$Important_Variable = $Cart->SomeList->list_of_objects[1];
$Important_Variable->important_output_function();
There. This might be perfectly functional code. Well, perfect in that it compiles and runs. Let’s go through all the trouble this setup might cause.
Problem 1 – Using the ObjectLister in this manner requires the code outside the ObjectLister object to intimately know the internal workings and arrangements of the ObjectLister class. Already we’ve upped the bar on future coding. New programmers will have to learn the inside of ObjectLister whenever they want to use it or want to debug the above code.
Problem 2 – What happens if we need to change how ObjectLister stores objects? What if we want to replace that internal array with a binary tree or something? Making that change will immediately break the Cart code in fatal (as in E_FATAL) ways. If ObjectLister is used in the manner of our example in many places across a large and complex program, then we’ve just created many, probably unique, breaks across an known number of features.
Problem 3 – What if some programmer later is working with this code, isn’t very familar with it, and adds this line of code in at the //1…2..skip a few part? (assume 1…2..skip a few is actually many lines of code).
$Cart->SomeList->list_of_objects[1] = new RandomObject(5);
Unwittingly, he has accidentally overwritten the object that ObjectLister was keeping track of in that spot. Lets say that our program is more complex than my example and that this overwrite is a real problem. Someone has the ‘bright’ idea of coding collision management code into the area of the Cart that this example sits. However, there’s all those other places that misuse ObjectLister! So all this new safeguarded code is just a band-aid on one part only.
Obviously I’m getting a little ridiculous here. But think about this approach in a large program, with many developers, over a timeline longer than 6 months, that undergoes changes in spec, that people are rushed on. Not uncommon! Crunch time has a tendency to send ideals out the window in favor of get-it-done. Time and multiple developers undermines codebase knowledge. Lots of factors can make stuff like this happen, even to the best of us.
So lets make one change and see how it shakes out.
class ObjectLister{
protected $list_of_objects; //an array of objects managed by this class.
public function addObject($object_to_add){};
public function getObject($identifier){};
}//end class
There! Now no outside code can directly access $list_of_objects. Sure all that stuff is going to have a problem suddenly, but that problem is no less systemic than “Problem 2” from above and what we’re doing is good for long-term program health.
Now we have cart code that looks like this…
$Cart->SomeList = new ObjectLister();
$Cart->SomeList->addObject(new RandomObject(1));
$Cart->SomeList->addObject(new RandomObject(2));
$Cart->SomeList->addObject(new RandomObject(3));
///1..2..skip a few….
$Important_Variable = $Cart->SomeList->getObject(1);
$Important_Variable->important_output_function();
This is better! If this was in all places in our code, we could safely change how ObjectLister works internally and we’d be fine. So long as addObject() and getObject() retain the same interface, we’re cool. Much more robust code. There’s still a problem though.
Hopefully you’ve realized that my ‘good’ example itself still violates Demeter’s Law. To write it, one must directly mess with the internal guts of the Cart object. We should really make $Cart->SomeList a protected variable with setter and getter methods as well. My example is a bit limited here, but this is what it might look like…
$Cart->initializeList(); //could this be in cart’s constructor? possibly!
$Cart->addRandomObject(1);
$Cart->addRandomObject(2);
$Cart->addRandomObject(3);
///1..2..skip a few….
$Important_Variable = $Cart->getRandomObject(1);
$Important_Variable->important_output_function();
What about the last two lines? Well, we could add a public function to cart to handle this. All it does is find the appropriate RandomObject and call its important_output_function().
$Cart->printRandomObject(1);
Finished code example.
class ObjectLister{
protected $list_of_objects; //an array of objects managed by this class.
public function addObject($object_to_add){};
public function getObject($identifier){};
}//end class
class RandomObject{
protected $value;
public function important_output_function();
}//end class
class Cart{
protected $SomeList; //objectlister list
public function addRandomObject();
public function printRandomObject();
public function initializeList();
….other stuff that carts do….
}//end class
//MAIN//
$Cart->initializeList(); //could this be in cart’s constructor? possibly!
$Cart->addRandomObject(1);
$Cart->addRandomObject(2);
$Cart->addRandomObject(3);
///1..2..skip a few….
$Cart->printRandomObject(1);
In Closing
Notice how much cleaner the main section of our program is to read and how obvious it is as to what each line does (information hiding in action!). We’ve got a solid structure here that embraces potential changes via firm encapsulation. If any of the implemented functions change their internal implementation, then the rest of this program will operate without a hitch. This happens to also have worked out to be well-stratified. Looking at cart, you see cart functions. Looking in cart you will see the ObjectLister and RandomObject functions. Since everything calls public functions of everything else, you can easily extend classes and substitute them into this structure wherever they are needed (modularity, extensibility) without extensive rewriting.
I hope that this has been useful to you and provided some concrete details about Demeter’s Law that you can take back with you. Lastly, just for reference. If you want to know more about the specifics of the Law of Demeter, I’d recommend starting at the wikipedia page for it.