Let's try to consider a revised version of the LawOfDemeter. Here is the rationale for revising it, and the gist of the refactoring of the "law". Anybody can help with a sexy formula for the revised law? Please come with something at least as punchy as the original LawOfDemeter. --CostinCozianu At first I jumped to say that LawOfDemeterIsInvalid. After all, if it was a valid law most of functional programs would have to be considered smelly or bad style, because all you do in FunctionalProgramming is navigate from the component of one structure to another, sometimes using recursion even. And there's the problem with very healthy looking lines of code like: void printDetailsAbout(Customer c) { // ... System.out.println("zip: " + c.getAddress().getZipCode()) // or Address a= c.getAdress(); System.out.println("city: " + a.getCity()); System.out.println("state: " + a.getState()); // and so on, so forth As per the original LawOfDemeter this code is found in violation of the law. But if we really are to judge this code as BadCode, first we'd have to fire all FunctionalWeenie''''''s, and lots of ObjectWeenie''''''s who often find themselves writing code as the above. And it works, and we don't get bitten by the dangers prescribed in LoD as punishments for breaking the law: like higher coupling, maintenance problem, difficulty in refactoring, etc. On top of that, all known "cures" for code like the code above can be judged objectively (that means we can count the beans!!!) as worse than the disease. Indeed there are naive solutions like adding a Customer.getCity(), Customer.getZip(), Customer.getAddressLine1() all they do, they increase the size of the interface for the Customer class, and while writing those forward methods you find yourself WritingDumbRepetitiveCode. Uggh, it reminds it of the old joke: "Doctor, it hurts when I do this! -- Then don't do that." There are more "advanced" fixes, like moving the code above into Customer.printDetailsOn(PrintStream s), in other words TellDontAsk, which WardCunningham himself blessed with "finally a version of the 'law' that I can believe in". But can TellDontAsk be usefully applied in many examples like the one above ? Arguably not, because again the cure for the law is worse than the disease. If we refactor the code above into a method like Customer.printDetailsOn(PrintStream s) then the resulting code has the following properties * '''increased coupling between the calling context and the Customer'''. Now customer needs to know that it should use print rather than println() , and how about a context that needs comma separated values for AddressFields. * added unnecessary responsibilities to the Customer class therefore '''decreased coherence'''. One can try even further by saying Customer.printDetailsOn(PrintStream s, CustomerFormattingOptions options) so that we can accommodate a large variety of client contexts, but then the code for that will be even more complex, and also the coupling will increase by adding an unnecessary entity and breaking DoTheSimplestThingThatCouldPossiblyWork, even for non-XP believers breaking DoTheThingThatMightWorkWell. * '''Worse adaptability to changes'''. Next time you'll need a client context that will output the zip code in some stupid JLabel (a label widget on the GUI). And what do System.out and JLabel have in common ? Nothing. So in Java, we might create an AddressConsumer common interface and ContextSpecificAddressConsumerAdapter. In dynamic languages (Python, Smalltalk) we'd be spared of one type entity but we may still have to write the damn Adapters. And for what, wasn't it simple when the client context just took the object Address a= customer.getAddress() and did whatever they pleased with it without bothering the Customer class to suport all their crazy needs ? The logical conclusion seems to be that at least in some contexts TellDontAsk as a fix to the LawOfDemeter can be, again, worse than the disease. I spared the reader (or did I ?) of other even more elaborate schemes to fix LoD violations, including deploying a PatternSoup to fix this disease (AcyclicVisitor anyone?). But the thing is, the more elaborate is the cure, the bitter it is. It feels like cutting your arm because you don't like your nails. Other problems of LawOfDemeter include a very fuzzy confusion between contructors and other kinds of methods, what is global and what is not, dealing with collection, what is collection and what is not, etc. So try repeat after me: "be lazy, be very lazy". Laziness is an outmost virtue in writing software. EconomyOfExpression trumps LawOfDemeter anytime (all other criteria being equal). If TheSimplestThing is to write System.out.println(c.getAddress().getZipCode()), then '''JustDoIt'''. It also happens to be the right thing. ---- None of the above-listed problems occur with a well-designed interface however. Instead of the customer ''printing'' its string representation, it should be sufficient to dynamically synthesize its string representation for the benefit of external programs. That is, the view object can invoke '''print(c.asMailingLabelString())''', or even, '''x := c.asCSV()''' and parse out the fields as appropriate. Strictly speaking, these are type-casting methods, converting from an instance of '''customer''' to an instance of a subtype of '''string''' in a type-safe manner. This keeps coupling to an absolute minimum while maintaining full cohesion, and does not violate TellDontAsk one iota, as individual fields are never queried. ---- Well, that's nice and dandy, quite an argument that blindly following the LawOfDemeter could get you in trouble, I said to myself. LawOfDemeterIsInvalid. '''Or is it''' ? Thanks to Daniel T. from UseNet comp.object for pointing out to me that all I did was not really to invalidate the law but coming out with more exceptions to the law, he gave me further clues with regards to both positives and negatives to the law. But if exceptions are so many and obvious then maybe the law is too general and needs to be revised. There has to be a grain of truth to the law if so many people defend it so fervently. On the other hand it was JonathanTang, I believe (please correct me Jonathan, that pointed out that the LawOfDemeter applies only to ObjectOriented designs and not to FunctionalProgramming design style. I first objected you cannot look at the same lines of code and judge it differently depending on the intentions and background of the programmer -- if it was a FunctionalWeenie or a OoWeenie, and what about MultiParadigmWeenie. But this gave me further an essential clue. And what led me to what I believe is the resolution to the dilemma is good old KristenNygaard. Let's ask ourselves: if we want to be able to '''break''' the law in examples like the one above. When we would '''not like''' for our clients to break the law against us ? Do not do unto others ... Well, one example that sprang immediately to my mind is the following. Imagine you have a GUI framework and your Dialog class has one event processing thread for each active Dialog. And then your client does this on you: dialog.getEventProcessingThead().interrupt(); // Ouch -- that really hurt and all the hell can break loose Well, one may wonder why's the getEventProcessingThread() public. Nevermind, assume there is a good reason. it shouldn;t be public but life's more complicated than that (or else we wouldn't need LoD of all).It can be because because the poor modular features if Java -- imagine a key implementation class in a separate packae -- say, gui.PlatformHook accesses the thread for a good reason through a dedicated interface from another package gui.widgets.Dialog and thus it can be no less visible than public. And other countless examples can be found where the LawOfDemeter '''is obviously valid'''. What happens then ? We don't like when our clients get a handle and '''mess around''' with some complex mechanism that we have for important object. It then turns out that the thing that both identifies and distinguishes legitimate exceptions to LoD is that they fit into different categories of objects/code as per NygaardClassification. If we look at objects as active entities that emulate entities as in a physical system (Nygaard's definition of OO) than for sure we do not like when a client context messes the internal mechanism of our objects. But on the other hand, we know from experience that we can't have all objects in a OO system be active, sometimes they are just PlainOldJavaObjects (or PoJo) that cannot be thought of as playing an active role in our OO design, but rather they are an expedient way to put things together. Object systems are typically a balanced mix of functional/procedural style data structures and active objects as per NygaardClassification''''''s, even when in languages like Java we have the data structuires wrap up in fancy classes like CustomerAddress. CustomerAddress is no active entity in your OO design, it's just a convenient way of grouping together some information in order to get a better structure. And when you have such a class you really don't mind in fact you '''should not be bothered to do something about''' when a client context wants to print the ZipCode formatted 10 spaces to the left or to the right. So the main insight that extended discussion with folks on wiki and on UseNet about LawOfDemeter is that the distinction should be '''semantical''', whereas the original LawOfDemeter made a mainly '''syntactical''' distinction (mainly against chaining x.m1().m2().m3() either directly or through intermediary local variables). So semantically, knowing the design of the system, we can differentiate the two case * When we have '''real objects''' (as per NygaardClassification), we do not want our clients to alter the functioning of our object mechanism, beyond what is exposed in their direct interface to the mechanism. If you have an interface to a Watch, you can change the hour, but don't try to directly change the shape or the layout of its dented wheels inside. If you need to accomplish such a feat, either ask the Watch to self-repair itself (if it supports such a feat), or take it to the WatchRepairShop * When we have just PlainOldData, we'd rather prefer to let our clients write System.out.println(c.getAddress().getZipcode()), rather than burden the poor Customer class with having to be coupled with all kinds of crazy needs of different client context. Simple and lazy is a virtue in this situation. Some fuzziness is still left on whether it is OK to '''modify the state''' of inner (contained) objects of PlainOldData. For example is it better to write: customer.getAddress().changeZipCode(newZipCode) or customer.changeZip(newZipCode); Of course, a MultiParadigmWeenie may also like to put his FunctionalProgramming suit and create a new Address altogether, rather than change the fields of the contained object. I think the first form is to be preferred in order to avoid burdening Customer class with unwarranted responsibilites, but I'm not as strong about it as I am about methods that do not modify the state. ---- Uncooked, but that's never stopped me before... Say only what you want others to know. Worry thyself not with why they would want to know it. --WilliamUnderwood ---- IsLawOfDemeterOverspecifiedOnCeeTwo ---- If we take the given example under the aspect of unit testing, the LawOfDemeter becomes more valid again. It is true, that is is often less work to write doSomethingWith(c.getAddress().getZipCode()); than to implement a Customer.getZipCode(); and to write doSomethingWith(c.getZipCode()); But if we want to do unit testing for our code, we would have to prepare two mock objects Customer and Address, if we don't apply the LawOfDemeter. If we apply it, we only need to prepare one mock object. El. ---- Why not add a VisitorPattern to the address class? So then you can write a visitor to convert the address to a string. So i end up calling something like: addressString = c.getAddress().execute(addrToStrVisitor); System.out.print(addressString); Benefits: - I can reuse the code from addrToStrVisitor whenever I need to print an address (rather than writing it over and over again) - I can extend the system with an abstract base address class to support different kinds of addresses (which is the whole point of Law of Demeter anyway), like foreign addresses where getZipCode doesn't even make sense. If we break Law of Demeter here, then we're assuming that the address structure can never change, which I think is a terrible assumption actually. -bdodson ''// the phrase:'' ''// c.getAddress()'' ''// is a violation of LoD, because you're querying an attribute.'' Ah, fair enough. Perhaps i should have added the visitor to the customer class and said something like c.execute(addrToStrVisitor); Nevertheless, I still think the point about localizability pokes a pretty big hole through the entire argument. -bdodson ---- The LawOfDemeter page specifies it in terms of only a parameter or field being valid as a receiver, and that you can invoke any method on a valid receiver, which means this would be a valid refactoring of the customer address example: class CustomerAddressVisitor implements Visitor { private Visitor
addressPrinter = ...; public void visit (Customer c) { addressPrinter.visit(c.getAddress()); } } class AddressPrinter implements Visitor
{ private PrintStream out = System.out; public void visit (Address a) { out.println("city: " + a.getCity()); out.println("state: " + a.getState()); } } What that might buy you is that it forces small methods with single responsibilities, and some separation of structure dependent methods and 'leaf' methods, though if you only transform paths to deeply nested calls you still have some coupling to structure. The alternative is to just use double dispatch and have the navigation internal to the customer. -- PeteKirkham