Dear David: Thank you for your detailed response. It was interesting to read your historical accounts. Your version of the LoD seems to be a sigificant improvement. It will take some time to appreciate your insights. Your addition is: all return values of methods are void. Our initial implementation of propagation patterns allowed only void return values. But we gave up on that based on pressure from the functional programming community. >From David.E.Smyth@jpl.nasa.gov Thu May 29 21:23:46 1997 >From: "David E. Smyth" <David.E.Smyth@jpl.nasa.gov> >To: Karl Lieberherr <lieber@ccs.neu.edu> >Subject: Re: LoD discussion > >Dear Karl, > >You wrote: >> >> Since probably several of our Web visitors are interested in >> your question and my response, I would like to ask you >> for permiision to post your question on the web. >> I would like to put it into: >> http://www.ccs.neu.edu/research/demeter/demeter-method/LawOfDemeter >> which contains several explanations by Brad Appleton of the LoD. > >Yes, this would be good. Great, I have created a new subdirectory called Smyth which contains this and related messages. > >I've got several bodies of code from 1990 to now which implement my take >on the concepts of LoD, which are very much different from the concepts >I can currently find on the Web. > >When I first heard of the LoD, I saw it as being very significant for >distributed/parallel/multi-threaded software, not so much for within a >given localized "small" chunk of code. > >I'm going to re-order your message somewhat in this response: > >First: there are more than just these two principles that fall out from >the Law of Demeter (as quoted by me, which is the "same" wording as the >FAQ answer in the early 90's): > > A method shall act only upon the arguments and the pre-existing > state of the object. Yes, this corresponds to what we called the object form of the LoD. There is also the class form. > >> You write in http://mds.jpl.nasa.gov/7thIEEE.html#Demeter: >> ==================== >> Note two things: First, the Law implies that no method >> shall return any value (otherwise the caller cannot obey the Law). >> Second, the Law is really just a description of dataflow processing >> in the context of objects. > >This reflects my take that its useful for >distributed/parallel/multi-threaded systems. > >> The wonderful thing about following this Law is that the result >> is a set of objects which can be multi-threaded, and distributed. > >As dataflow is so easily distributed and computationally efficient on >multiple processors (or in multiple threads). > >> Temporal coupling and synchronization is eliminated, ... > >because no return values, the "sender" cannot tell when, or even if, the >receiver gets the message. > >> ... therefore avoiding three of the nastiest problems in real-time >> and distributed systems: critical sections, priority inversions, and >> deadlock. > >critical sections, because each object actually defines a critical >section: a non-atomic set of data which must be kept consistent. I see, your objects are monitors. > >priority inversions that occur due to critical sections simply don't >happen. > >deadlock: well, in three of four cases, they are eliminated. The four >cases: (1) two or more clients need to read one section; (2) two or >more clients need to read multiple sections; (3) two or more clients >need to write one section; (4) two or more clients need to write >multiple sections. The cases (1) and (2) never happen simply because of >the law itself (never read anyone else - you subscribe to their data, so >you already have it as state or as arguments). Case (3) never happens >because that critical path is encapsualted in some object which runs >asynchronously, and just does one request and then the next. Case (4) >can occur: say two objects A and B must each uniquely control objects >x,y,z. Clearly, a deadly embrace can occur due to race. > >> ==================== >> end of quote from http://mds.jpl.nasa.gov/7thIEEE.html#Demeter >> >> In our view it is essential that methods can return values. >> For example, in "this.get_x().f()" get_x() returns the data member >> x and calls f on it. Or it could be: "this.compute_x().f()" >> in case of a derived and not stored part. > >In our reading of LoD, no return values ever are provided anywhere. To retrieve a data member it is natural to call an accessor function with a return value. How do you do that? > >As I said before, there are actually several wonderful properties that >fall out from this definition of LoD: > > A method can only act upon the message arguments, and the > existing state of the receiving object. > >1. Method bodies tend to be very close to straight-line code. Very > simple logic, very low complexity. > >2. There must be no return values, or else the sender of the message > cannot be obeying the law. > >3. There cannot be tight synchronization, as the sender cannot tell if > the message is acted on or not within any "small" period of time > (perhaps the objects collaborate with a two way protocol, and the > sender can eventually detect a timeout). > >4. Since there are no return values, the objects need to be > "responsible" objects: they need to handle both nominal, and > forseeable off-nominal cases. This has the wonderful affect of > localizing failure handling within the object which has the > best visibilitiy, and understanding, of whatever went wrong. > It also dramatically reduces the complexity of protocols, and > clients. > > For example, with POSIX file system calls, one needs hundreds > of lines of code to fully handle all possible failures of the > open() call. This code needs to be at every open() call in all > clients, because the POSIX file system is not responsible. > > A responsible object would deal with the forseeable off-nominal > cases. Then all clients benefit. > > Clearly, something outside of the responsible object may need to > act if the responsible object cannot resolve a problem with local > action. OK - then a wider scope object needs to be involved: the > responsible object sends the problem message "up" to some object > with broader visibility. True, this might be, in a trivial system, > the client -- but the problem is reported as a different message, > not a return value from the original message (don't think > continuations, because we are talking distributed here). > >5. The law requires an object to subscribe to information, so it has > what it needs whenever it gets a message. This means that lazy > evaluation can't be used. While this may seem like an inefficiency, > it only becomes one in practice if the objects don't have concise > responsibilities. In such a case, efficiency of communication > bandwidth isn't the real problem. > > There are a couple of caveats here: if some information is almost > never needed, and is expensive to compute, then perhaps the LoD can > be broken. > > We have found, however, that breaking the LoD is very expensive! On > Mars Pathfinder, the integration costs of the law breaking parts of > the system were at least an order of magnitude higher, and we had > to pay that cost on every integration cycle -- not just once! We > should have paid the cost once by implementing a more sophisticated > protocol that did obey the LoD. A very interesting. > >6. Since tight syncronization is out of the picture, the responsible > objects should be goal oriented. A goal is different from a method > in that a goal is pursued over some expanse of time, and does not > seem instantaneous. By thinking of goals rather than discrete > actions, people can derive solutions which don't require tight > temporal coupling. This sounds like hand waving, and it is -- but > 7 years of doing it shows it really does work. > > >> Today I view the LoD as a style rule about structure-shy programming. >> It says that we should not encode too many details of the class >> structure in our methods. Each method should only reveal structural >> details of a limited set of classes. >> (AP book: Chapter 8, Section 8.1. Connection to the Law of Demeter.) >> >> While the LoD is still a valuable rule for oo programming, >> it reveals a flaw in oo programming: Class structure details >> need to be encoded repeatedly into the method bodies. This leads >> to highly redundant software which is brittle with respect >> to structural changes. >> (AP book: Chapter 1, Section 1.2. Disadvantages of oo software.) > >I agree with you that details of class structure can be a big problem. >However, my approach to this problem is rather draconian -- just avoid >inheritance outside of specific, well defined, and separable areas. >Wait! Don't barf! Let me explain: I like your approach to avoid inheritance. But let me remark that by class structure we mean the entire class diagram, including all associations. Think of a UML class diagram. > >My background in OOP began with Mesa in 1985 (I was the UNIX guru on a 6 >person team to implement a dual mode SVID/BSD compliant UNIX kernel >which was object-oriented to the floor in Mesa -- we succeeded in 6 >calendar months). That effort was at Siemens ZTI in Munich, and involved >cooperation with Xerox PARC. We therefore had access to the PARC library >of "blue papers" which included some very interesting lessons learned >from their development of many OO systems over the previous 10 or so >years. > >The papers on Smalltalk were very interesting: The Smalltalk system was >developed by a close knit team of 4 or 6 brilliant people (we know them >all). They started from ground zero in January, and by August, they had >a complete network aware OS, development environment, desktop tools, and >so on. They were justifiably proud, and wanted to get some graduate >students in on what they were doing. They brought in some extremely >bright people from Stanford and Berkeley, and gave them jobs that each >of the core team felt could be done in a few hours, or perhaps a whole >day. After a few days, or a week or two, they found that in fact the >students hadn't done anything. An example of an assignment was a news >reader. The core team member sat down with the student, and in an hour >evolved the existing mail reader into a news reader. See? Easy! > >At the end of the year, the team got together, and reviewed their >accomplishments. They were pleased with what they had pulled off: noone >had ever done anything remotely like that before in a year with 4 (or 6) >people! But they were concerned that the very bright students couldn't >do anything with Smalltalk. Hmmm, they decreed: "Do it once, then do it >right!" was the lesson learned. > >So, in January, they again started at zero, but with a year of >experience. In August, they had again an extensive system. They again >brought in a fresh batch of the brightest students from Berkeley and >Stanford. Again they gave them easy, one day jobs... that again were >never solved by the students, yet were done by the core team in an hour >or so... > >They were perplexed. They decided that, well, perhaps object-oriented >software was EVENTUALLY more productive (by far), but that there was a >steep learning curve. > >Another document provided the history of PARC attempts to sell their >object-oriented windowed environment to metal benders like DEC. DEC >declined to buy over the learning curve issue: they felt, probably >correctly, that their customers would not like the fact that the >programmers would get nothing done for, say six months, and then get >alot done, because the turnover in the software ranks was so high: a >programmer would do nothing for 6 months, and then go somewhere else as >soon as they finally became productive. > >By 1985, when we did this UNIX thing, the concept that OOP had a steep >learning curve was just a commonly accepted fact. There was no consensus >on why this was so, but there were two obvious problems: first, it was >hard to find exactly what piece of code implemented a method; second, it >was recognized that changing the method at the wrong layer in the >inheritance tree had grave consequences. But time seemed to heal all >things: the longer programmers worked with a given object tree, the less >problems they had. But boy, did they have problems early on! > >At the OOPSLA in New Orleans (I think it was 88), there was a panel >discussion titled "Inheritance: Can we have our cake and eat it too?" On >the panel were all the glitterati: the founders of Smalltalk, LOOPS, >Flavors, Eiffel, Objective-C, C++. Only one person felt inheritance was >worth saving: Bjarne, who was just about to release MI in C++. Lots of >problems were mentioned: you could list them as well as I could. A key >problem was that methods are implemented assuming a specific class >context. Change the class (subclass it) and it becomes hard to know if >the non-overridden methods will still work, without actually looking at >them in detail. Override a method, and maybe you are changing some >semantic which is assumed by another non-overriden method. Therefore, >the coupling between the code is not just the syntax of the interfaces, >but the semantics of the implementations. As you AP papers mention, an >exposed class structure is a problem! > >At either that OOPSLA, or the OOPSLA in San Diego (89 I think), there >was a paper on a very extensive distributed Smalltalk system for the >Canadian Navy. They found that as their development effort went on, >their inheritance tree got flatter and flatter -- the opposite most >would expect! Why? Because when you don't really understand something, >as is true early in an effort, one tends to see the similarities between >things -- inheritance seems the way to go. However, as one really starts >to understand the system (later in development), the details which cause >things to be different become significant, and things start being less >and less alike. Early on, their average depth was something like 12 >deep, by the time they deployed it was 2.1 deep (and 2 is the minimum >depth: everything is derived from Object). > >Therefore, when I start a big system (and my systems usually take a few >years), I just start out without inheritance, so I don't need to hassle >with flattening the tree later on. Very interesting. We instead start with a grammar and some of the non-terminals naturally map into abstract classes. > >Instead of inheritance, I use collaborating responsible objects. This >allows the collaboration to be based on syntax, rather than semantics. >Far looser coupling. > >And the interfaces between the objects? LoD!! > >Oh, we can come up with cases where inheritance is good: GUIs are a >classic. But this is perhaps because people use them in very specific >ways. Still, extending GUI classes is often a nightmare. Within a given >responsible object, inheritance is often useful. > >The Java package concept (modelled after the re-worked Smalltalk >environments) seems a reasonable approach: one can't generally discern >the class structure outside of the package -- its not the clients >problem. > >> Therefore, we invented traversal specifications (now called >> traversal strategies) which allow for a much more structure-shy >> approach to programming. >> >> Traversal strategies were first used in conjunction >> with propagation patterns >> (AP book: Chapter 8, Propagation Patterns.) >> and now in conjunction with visitor objects. >> http://www.ccs.neu.edu/research/demeter/sources/DemeterJava/demo/icse/doug.html > >Thanks for the pointers into your book and these papers. I'll do some >reading over the next few days. > >> The Demeter/Java system is a new implementation of the Demeter >> ideas for Java. The latest source code is in the youngest subdirectory of: >> http://www.ccs.neu.edu/research/demeter/sources/DemeterJava/inJava/ >> (currently 0.4.6-JDK1.1). For general information on >> Demeter/Java, see: >> http://www.ccs.neu.edu/home/lieber/Demeter-and-Java.html > >I'm asking Dr. Dan Dvorak, one of the X2000 software team members, to >specifically look into this. > >> I agree with your point that simpler is better but traversal >> strategies are simple and they simplify both initial program >> development and program maintenance. >> >> Please let me know whether the arguments and pointers above >> convince you of the value of traversal strategies to complement >> (NOT replace) the Law of Demeter. > >Will do! Thanks! > >Oh: one more thing. I seem to remember a one page article on LoD in >something like ACM or IEEE Computer sometime around 1988. Do you >remember anything like that? Yes, you mean. @ARTICLE{LHLR:law-letter, AUTHOR = "Karl J. Lieberherr and Ian Holland and Gar-Lin Lee and Arthur J. Riel" , TITLE = "An objective sense of style", JOURNAL = ieee-computer, MONTH = "June", YEAR = "1988", NOTE = "Open Channel publication" } > >Also, I am certain that comp.object discussions of LoD frequently were >in the context of distributed/multi-threaded/parallel systems, which is >what I was doing at the time. Do you know of any archive of such >discussions? No, I don't. > >-- >- David.E.Smyth@JPL.nasa.gov http://mds.jpl.nasa.gov/David.E.Smyth >-- Snail: JPL, MS 303-310, 4800 Oak Grove Dr, Pasadena CA 91109 >--- Office: 303-310W -- Voice: (818)393-7944 -- FAX: (818)393-5013 >---- >----- Fault Tolerant Parallel Distributed Martian Web Software, etc. >------ Java + JavaOS + JavaScript + HTML + cgi-bin + perl + Darkstar >------- X2000 -- CISM -- FST -- NM DS1 -- DNP -- MPF > > "I am convinced that life is 10% what happens to me and 90% how > I react to it. And so it is with you..." > Charles Swindoll > Looking forward to our further discussions. -- Karl