forward: compmail to: g.gruman Here are the revisions to your major rewrite. I found your questions and remarksextremely helpful. Often I have put the answers directly into the text without giving an explanation to you. I won't be in on Monday: therefore I send you the revisions today. There are probably some rough spots here and there which need some smoothing. Please can you help in identifying them. |kl: starts my remarks| |kl: General: What is between \begin{definition} \end{definition} should be high-lighted because it is an important definition. | -- Karl Lieberherr LIEBER.DOC Lieberherr/Holland IEEE Software September 1989 |au: My comments precede the paragraphs they refer to.| |au: Your article was written as a series of (sometimes nested) listed and enumerations, which made it very difficult to read, especially in the first half. This imposed structure essentially overwhelmed the natural flow of meaning. The transition information which serves as the current or flow to guide the reader from point to point was lacking or supplanted by the frequent and sudden unexplained shifts in focus. I've tried to make the flow clear, but in several places I could not. I've marked these out to you. Throughout, please ensure that I captured the flow correctly. You also explained most concepts after introducing them (and sometimes after using them), which detracted severely from the meaning of the concepts as used.| |au: Please verify that the references are correctly cited. In moving material around, I had to renumber the references several times, and I may have made mistakes.| |au: The deck below is meant to intrigue readers, not summarize the article. Feel free to suggest alternatives. The maximum length is 27 words.| |kl: good deck| [ds1]The language-independent Law of Demeter encodes the ideas of encapsulation and modularity in an easy-to-follow form for object-oriented programmers. |au: I suggested a new headline. The original headline was too long to fit, but simply removing the "Object-Oriented Programming" preface (which was my first inclination) left another problem: very probable confusion with the identical headline used in Computer a year ago and in the OOPSLA proceedings last year.| |kl: good title| [hs0]Assuring Good Style for Object-Oriented Programs [bs1]Karl J. Lieberherr <>and<> Ian M. Holland<>, Northeastern University [ks1]When is an object-oriented program written in good style? Is there some formula or rule that you can follow to write good object-oriented programs? What metrics can you apply to an object-oriented program to determine if it is good? What are the characteristics of good object-oriented programs? |au: I moved the brief description of the law to the introduction so the reader knows the goals of the style that you intend to describe in the rest of the article. Otherwise, the reader will have difficulty meeting the challenge that ends your introduction. Still, several points are not clear. How do you apply these rules? Who determines limits like messages a method can send to a set of objects? How are these determinations made? How do you know if you've succeeded in defining or using them? It is important to make sure that you explain in the article how you accomplish the three aspects of the law described below.| In this article, we put forward a simple law, called the Law of Demeter, that we believe answers these questions and helps formalize the ideas in the literature.[fo]1,2[fx] There are two kinds of style rules for object-oriented design and programming: Rules which apply to the structure of classes and rules which apply to the way methods are written. Here we focus on style rules which restrict the way methods are written for a given set of class definitions. The Law of Demeter restricts the message sending structure of methods. Style rules for the structure of classes are given, for example, in [fo]3[fx]. Informally, the law says: |au: How do you restrict this message sending? Via the protocols described later? How do you determine the object set? What are Self and This? I assumed they are language tokens, but I suspect many readers will not know what they are without some brief description (such as "the Self originating object"). You should also identify the language for which Self is a token.| [cb] Each method can send messages to only a limited set of objects: to argument objects (including self) and to the immediate subparts of self (self, from Smalltalk and Flavors, is called this in C++ and Current in Eiffel). |kl: limiting inheritance is not covered by the law| |au: The meaning of "organize dependencies" and "organize collaborations" is unclear. Are you giving an example of code? Or are you rephrasing the previous sentence? If the former, be explicit about what you are doing; if the latter, drop the parentheticals. My boss thinks you mean limit dependencies in the first instance and limit inheritance in the second; I thought the second meant limit globality. How do you limit these dependencies? With the acquaintance classes described later?| [cb] The following is a rephrasing of what the law says: Each method depends on a limited set of objects (organize dependencies). |au: How do you limit collaboration? With preferred suppliers?| % [cb] Each method collaborates with a limited set of objects %(organize collaborations). The goal of the Law of Demeter is to organize and reduce dependencies between classes. Informally, one class depends on another class when it calls a function defined in the other class. We believe that the Law of Demeter promotes maintainability and comprehensibility, but to prove this in absolute terms would require a large experiment with a statistical evaluation. Because the field of object-oriented programming is relatively new, large object-oriented software developments that can provide data on the benefits of handling dependencies better, are rare. However, we have examined our own code (about 14,000 lines of Flavors and C++ code) and are convinced of the law's benefits. |au: Check my transition at the end of the paragraph to set up the presentation on style in the later paragraphs. I moved the Demeter material to a sidebar. This keeps the article focus on the law. I also moved the definitions specific to Demeter into a separate sidebar; again, the intent is to focus on the law rather than the example environment.| We developed the law during the design and implementation of the Demeter system and therefore we named it after our system, which provides a high-level interface to class-based object-oriented systems. (Demeter is briefly described in the box[sa1] on p. [bo]XX[bx]. The examples in this article are written in Demeter notation, which is explained in the same box.) Such a high-level interface supports an environment where code may evolve continuously rather than in sporadic jumps. |au: I genericized the next two paragraphs so the law's applicability beyond Demeter is no longer an issue to be raised and explained. In sentence 2, you first mention the word "style." You need somewhere to explain the relationship between the Law of Demeter and good style, even if it is only that following the law will result in good style. It would help if you define attributes of good style or at least say that the informal summary of the law above defines both the law and (some) good style. You touch on that in the last sentence but only in relation to Demeter. The paragraph following this one ("But this issue ..." seems a natural place to do so.| To achieve this continuous evolution, we would like programs to be well behaved or well formed in some sense. In other words, we would like the programs to follow a certain style that lets them be modified easily, minimizing changes required elsewhere in the programs. This ease of modification is one criterion that characterizes a good object-oriented programming style. Following the Law of Demeter will result in good style, provided the programmer follows other well-known style rules such as: minimize code duplication, the number of arguments, the number of methods etc. Every object-oriented programmer should know what is considered good object-oriented programming style just as procedural programmers are aware of the top-down programming paradigm, the "thou shalt not use a goto" rule, and others.[fo]3[fx] Many of the style rules for procedural programming are also applicable to object-oriented programming. In an earlier paper,[fo]10[fx] we presented a proof that states that any object-oriented program written in bad style can be transformed systematically into a structured program obeying the Law of Demeter. The implication of this proof is that the Law of Demeter does not restrict what a programmer can solve, it restricts only how he solves it. We challenge object-oriented programmers to check if their programs follow our law and, where they do not, to consider whether they should. We believe all programmers who use object- oriented programming techniques should adopt our law. |au: The original headline covered clients, suppliers, and dependencies, but this didn't fit with the focus of explaining the use of the law from three perspectives: class, object, and minimization. For continuity, I also moved the text explaining the three forms of the law that was originally at the end of your introduction because this section seems to explain how each form in turn works and applies. To reinforce this structure, I've used subheads to more clearly identify which form is being explained. In the introduction to this section, you still should explain why there are three forms and briefly why this is important to know.| [hs1]Forms of the law |au: This section is not clear. How many laws are there? One law with rules for different aspects of program design? Are class, object, and minimization arranged in order of level of abstraction? Make it clear why there are three forms and how they relate to using the law. It might help to explain the structure of language components from high-level elements to low- level. You would explain that there are classes of whatever made up of objects, which do whatever and which communicate to other objects through protocols (or whatever).| The Law of Demeter has three forms which are summarized in the following figure: \bv ---- strict form class form -| ---- minimization form object form \end{verbatim} The class forms are expressed in terms of classes and can be supported by a law-enforcement tool. The strict form is a special class form which rigorously restricts the dependencies between classes. There are situations where we need more flexibility we have the minimization form which is a special class form. The minimization form allows additional dependencies between classes but it asks that we minimize them and document them by declaring so-called acquaintance classes. It is straight-forward to modify a compiler for a statically typed language such as C++ and Eiffel to check adherence to the strict form of the Law of Demeter. But such compliance at the class level does not guarantee adherence to the [io]spirit[ix] of the law. Therefore, we have the object form of the Law of Demeter which expresses what we really want. The object form cannot be enforced at compile-time. |au: Class is the lowest level of abstraction? Later, you say that to truly enforce the spirit of the law, you must build in compiler extensions to check if objects are handled appropriately, so why is compile-time enforceability mentioned with classes? I've tried to explain it based on my reading of the article.| % [cb] Class: The class form expresses xxxxxx. By extending a %compiler to check of adherence to the class form of the Law of %Demeter, you can enforce compliance at compile time for %statically typed languages. But such compliance at the class %level does not guarantee adherence to the [io]spirit[ix] of the %law. % [cb] Object: The object form expresses what you actually %want but is hard to enforce at compile time. Still, you can %extend a compiler to check compliance at this level. %|au: This is unclear. Explain what you mean by controlled %exceptions.| % [cb] Minimization: This is the most flexible form of the Law %of Demeter because it allows controlled exceptions. The minimization form the law is the easiest to define and is therefore given first. We need three definitions to formulate it (see sidebar MIN). --------------------------------------- sidebar MIN C-object -------- \begin{definition} A C-object is an object belonging to class C, i.e., a C-object is an instance of a subclass of C. \end{definition} Client and supplier ------------------- \begin{definition} A method M is a client of method f attached to class C if inside M message f is sent to a C-object or to C. Exception: if f is specialized in one or more subclasses then M is only a client of the f attached to the highest class in the subclass hierarchy. A method M is a client of class C (and C a supplier of M) if M is a client of some method attached to class C. \end{definition} Acquaintance class ------------------ \begin{definition} A class C1 is an acquaintance class of a given method M attached to class C2, if C1 is a supplier to M, and C1 is not \begin{itemize} \item an argument class of M, including C2 \item an instance variable class of C2 \end{itemize} or a superclass of the above classes. \end{definition} Minimization form of Law of Demeter ----------------------------------- \begin{definition} Minimization form of Law of Demeter: Minimize the number of acquaintance classes over all methods. \end{definition} endsidebar MIN ---------------------------------------- |au: What is the relationship of a protocol and a class? How do you determine what constitutes a protocol? Does the programmer do this? How does he judge what is appropriate? I get the impression that the goal is to restrict this protocol somehow. Explain how. If protocols do something else, explain what they do and how. Then go into the program example. Also, protocol has only two messages? Are m1 and m2 sets for, respectively, classes and its instances? Or do you mean "methods"?| |au: [nd] is a wide hyphen we use to distinguish the keyboard hyphen from the typographic one. I replaced your use of "class C" with "class B" and of "method M" with "method m1" to keep the number of variables down. You are not referring to actual classes or methods, so it seems to make sense to stick with the same variable identifiers here.| %A method uses %the protocol of a class [io]B[ix] if inside the method a message %[mx]Method [io]m1[ix] is attached to class [io]B[ix]. The return %type of [io]m1[ix] is class Ident. To send a message, you use the %C++ function-call notation ("[io]s[ix][nd]>[io]f[ix]()" is the %same as "send [io]s[ix] the message [io]f[ix]"). A method uses %the protocol of a class [io]B[ix] if inside the method a message %is sent to an object in class [io]B[ix] or to [io]B[ix]. If %method [io]m1[ix] uses the protocol of [io]B[ix], method %[io]m1[ix] is a client of class [io]B[ix] and [io]B[ix] is a %supplier of method [io]m1[ix]. %|au; This needs to be better tied in to the definition. Why is %this a relevant issue here? I've moved it out of the definition %paragraph for better placement or integration.| We view the instance variables of a class to be private (in the C++ sense) and, if necessary, accessible through public methods for modification or reading. % According to this definition, a method that gets passed an %object of class [io]B[ix] and that passes the object further %without directly sending a message to the object or to [io]B[ix] %is [io]not[ix] viewed as a client of class [io]B[ix]. |au: In sentence 1, is it the Smalltalk approach or the definition that lets you send a message to its class? Earlier, you said protocols are what send messages to classes, so I'd expect some mechanism to let you send messages, not a definition or approach. Please clarify. I added "For example," to the end of this paragraph to like the program segment to the text that (I think) referred to it. If there is no connection, write an introduction telling the reader what you are showing.| The definition of C-object uses the Smalltalk-80 approach, which lets you send a message to a class. Sending a message to class [io]B[ix] activates a class method of [io]B[ix] that might, for example, create instances of [io]B[ix] or initialize a class variable of [io]B[ix]. The following example motivates the client/supplier definition: |kl: V,>,< are arrows| \begin{example} \label{cl-def} This example (see Fig. Client definition) motivates the client definition: \begin{figure} \bv Inheritance hierarchy: (A is highest) A | V inherits from | B has g() attached | V | C has virtual f() attached | V | D--->---F has h() attached | V | E has f() attached (overrides) class X has parts c : C implements interface m() {calls C -> g(), // call of g for C-object C -> f(), F -> h()} end class X. \end{verbatim} \caption{Fig. Client definition} \end{figure} m is a client of classes B, C and F. D is excluded since it does not have a method attached which could be called in m. E is excluded since it is a specialization of f in C. \end{example} %For %example, %[mo]class B has parts % x : Ident % implements interface % m1() returns String %end class B. %[mx]If method [io]m1[ix] sends a message to [io]x[ix] then %[io]m1[ix] is a client of Ident and class Ident is a supplier of %method [io]m1[ix]. |au: By "system," do you mean Demeter or any object-oriented system? What acquaintance classes really are is unclear, yet they seem to be very important. Are they just preferred suppliers? I suspect not from later text. Also, what do protocols have to do with defining an acquaintance class? Or is a protocol the explicit set of declarations in a class's header?| [io]Acquaintance classes.[ix] Every class in an object-oriented design or program is a potential supplier of any method. However, it is best to limit a method's suppliers to a small set of preferred classes. To define these preferred supplier classes, we introduced the concept of an acquaintance class.[fo]5,6[fx] A method's acquaintance classes are expected to be declared explicitly in the method's heading. %and are added to %the method's preferred suppliers. For example, class [io]C1[ix] %is an acquaintance class of method [io]M[ix] attached to class %[io]C2[ix] if [io]C1[ix]'s protocol is used in [io]M[ix] and %[io]C1[ix] is not % [cb] the same as [io]C2[ix], % [cb] an argument class of [io]M[ix], or % [cb] instance variable class of [io]C2[ix]. %|au: Counted by what for what purpose?| For the minimization form of the law we count the number of acquaintance classes for all methods. If a class appears as an acquaintance class of several methods, it is counted as many times as it appears. |au: Acquaintance classes are used to do only these typical uses?| Acquaintance classes are typically used for three reasons: [cb] Stability: If a class is stable or if its interface will be kept upwardly compatible, it makes sense to use it as an acquaintance class in all methods. The user specifies such global acquaintance classes separately and they are included in all acquaintance classes. [cb] Efficiency: To gain efficiency the user might need access to the instance variables of other classes. In C++ terminology, these are classes of which [io]M[ix] is a friend function. [cb] For object construction. |au: The following explanation of acquaintance classes was originally in the "Valid violations" section later, but it seems more relevant here. But I'm confused by an unstated implication: Are acquaintance classes by nature violations of the law (why else would they be flagged?)? Explain how acquaintance classes promote compile-time enforceability: by letting you specify which dependencies are to be enforced? Also, in sentence 2, who warns the programmer? Demeter? Keep the distinction between the Law of Demeter and the Demeter implementation clear. I tried to make this differentiation clear here by altering sentences to alert the reader that you are talking about an instantiation of the law, not the law itself. In the last sentence, what may be suppressed? All messages? Acquaintance-class-related messages? Programmer-defined messages? The implication is acquaintance- class-related messages because this paragraph deals with this class.| If a statically typed language such as C++ or Eiffel are extended with a facility to declare acquaintance classes, it is straight-forward to modify the compiler to check adherance to the minimization form in the following sense: Each supplier which is an acquaintance class is declared in the list of acquaintance classes of the method. % Acquaintance classes support systematic adherence to the law %that a system like Demeter can enforce at compile time. When a %method has acquaintance classes, Demeter warns the programmer %that there are unusual dependencies between the classes %(dependencies the Law of Demeter usually prohibits). Demeter's %compile-time law-enforcement program has the information %necessary to suppress law-violation messages for xxxxxx. To easily check the law at compile time or even at design time, the user must provide the following documentation for each method: (1) the types of each of the arguments and the result and (2) the acquaintance classes. The documentation gives the reader of the method a list of types he must know about for understanding the method. The law-enforcement program must track the following additional information about each method: (1) the message sendings inside the method and (2) the classes of the objects created directly by the method. |au: This is not clear. I get the impression that acquaintance classes are a necessary evil, one that should be avoided and can be avoided for any program that does not create objects. If a program must create objects (and wouldn't most object-oriented programs do so>), acquaintance classes are the way to do so, although there is some unspecified price. Yet earlier you said they were preferred suppliers -- which seems to be mandated by the part of the law that says to keep interactions/collaboration to a minimum. Again, explain what an acquaintance class is, why it is used, and what the implications are for its use and nonuse.| We proceed now to the definition of the strict form of the law which is given in sidebar STRICT. ------------------------------------------ sidebar STRICT Preferred acquaintance class ---------------------------- \begin{definition} Class C is a {\em preferred acquaintance class} of method M if it is an acquaintance class of M which is either a class of objects created directly in M (by calling the constructor of C) or a class of a global variable used in M. \end{definition} Preferred supplier class ------------------------ Class B is a preferred supplier of method m1 \begin{definition} Class B is called a preferred supplier of method M (attached to class C) if B is a supplier of M and one of the following conditions holds: \begin{itemize} \item B is an instance variable class of C or a superclass of such a class \item B is an argument class of M, including C or a superclass of such a class \item B is a preferred acquaintance class of M. \end{itemize} \end{definition} Strict form of Law of Demeter ----------------------------- \begin{definition} Law of Demeter (strict form): All methods may only have preferred supplier classes. \end{definition} end sidebar STRICT ------------------------------------------ You can write object-oriented programs with a restricted set of acquaintance classes called preferred acquaintance classes (this was shown in [fo] 11 [fx]). The strict form of the law says that the only acquaintance classes which should be used are the preferred acquaintance classes. The parts (instance variable) and argument classes together with the preferred acquaintance classes are called the preferred supplier classes of a method. The relationship between acquaintance classes and preferred supplier classes is given in Figure ``Structure of supplier classes''. \begin{figure} \bv ----------------- <- | parts and | | | argument | | | classes | | preferred | | | supplier | | | classes | | | | | | -> |---------------| | | | | | | |---------------| <- | | | acquaintance | | acquaintance | classes | | classes | | | to be avoided | | | | | | | -> ----------------- \end{verbatim} \caption{Structure of supplier classes of a method} \end{figure} The supplier classes of a method consist of the union of the preferred supplier classes and the acquaintance classes. The intersection of the preferred supplier classes and the acquaintance classes is in general not empty: it consists of the preferred acquaintance classes. |au: You need to explain what you're setting up here. I guessed you were setting up an example.| % [io]Class form example.[ix] Consider an example of classes. %Class [io]B[ix] is called a preferred supplier of method %[io]M[ix] (attached to class [io]C[ix]) if [io]B[ix] is a %supplier of [io]C[ix] and one of the following conditions holds: % [cb] [io]B[ix] is an instance-variable class of [io]C[ix]. % [cb] [io]B[ix] is an argument class of [io]M[ix], including %[io]C[ix]. % [cb] [io]B[ix] is a special acquaintance class of [io]M[ix] %where [io]B[ix] is either a class of objects created directly in %[io]M[ix] or [io]B[ix] is a class of a global variable used in %[io]M[ix]. %|au: I assumed that the second sentence was an example of a %preferred client, so I added the transition "For example," to %make that connection clear. OK?| %|kl: later % To match the definition of preferred suppliers, there is a %corresponding definition of preferred clients. %Method [io]M[ix] is a preferred client of class [io]B[ix] if %class [io]B[ix] is a preferred supplier of method [io]M[ix]. |au: Figure 1 is the original Example 3. I removed your general- store figure because it did not explain the terminology better than the terms themselves do. (In fact, its labeling the preferred-supplier relationship of the mail-order catalog to customer as "illegal" was confusing, since shopping sources are rarely illegal in the usual sense that the general-store analogy would have a reader use.)| To send a message, we use the C++ function-call notation ("[io]s[ix][nd]>[io]f[ix]()" is the same as "send [io]s[ix] the message [io]f[ix]"). In the five examples in Figure 1[fa1], class [io]B[ix] is a preferred supplier of method [io]M[ix]. Comment lines begin with a semicolon. |au: You originally had a paragraph labeled "Law of Demeter," which I tried to integrate into the text. You should consider pulling all your laws of Demeter into a sidebar for easy reference and also explain the various laws with examples in the main text. Are these separate laws or variations on the basic law? It was and is not clear. Also, the part about how the law may be misleading for metaclasses is confusing. I didn't see how the law interfered with your ability to make instances of classes wherever you like -- it doesn't address anything but the type of classes a method may have. Is this really an issue? If you dropped the last two sentences, would anyone be misled or confused? If so, you need to explain how the possible misinterpretation or confusion would arise.| % One Law of Demeter is that all methods may have only %preferred supplier classes. Because a class's protocol includes %both messages to objects and to classes, the law restricts the %use of both metaclasses' and classes' protocols. (A metaclass has %instances that are classes; this terminology comes from %Smalltalk.) Restricting the protocol of metaclasses might be %misleading because you need to be able to make instances of %classes wherever you like. Indeed, a close inspection of the law %reveals that it does not impose any restriction on where you may %make new instances. |au: This seemed to be class-form-specific, so I moved it here. If it applies to all forms of the law, rewrite it to cover all three and move it down after this "Forms of the law" section.| [io]Benefits.[ix] Applying the strict form of the Law of Demeter has several benefits. One of those benefits is stated precisely in sidbar BENEFIT. ----- sidbar BENEFIT Preferred client: ----------------- \begin{definition} Method M is called a preferred client of class B if class B is a preferred supplier of method M. \end{definition} Benefit: -------- \begin{definition} If the strict form of the Law of Demeter is followed and if the interface of classes \( C _1 , C _2 , ... , C _n \) is changed, then only the preferred client methods of \( C _1 , C _2 , ... , C _n \) require modification. \end{definition} endsidebar ---------------- % [io]Benefits.[ix] Applying the class form of the Law of %Demeter has several benefits. % For example, if you follow the Law of Demeter and if class %[io]A[ix]'s protocol is changed, only the preferred client %methods of [io]A[ix] and [io]A[ix]'s subclasses require %modification, provided that the changes implied in the preferred %client methods do not change the protocol of those classes. This benefit clearly shows that the Law of Demeter limits the repercussions of change. We use a set of classes in the formulation of the benefit since it is often to change the interface for a group of classes (because of function specialization there are dependencies between signatures). The interface of a class can be changed in many different ways. Some examples are: An existing signature is modified by changing an argument or return type, or by adding or deleting an argument or by changing a name of a method or by adding or deleting a method. Using the law can also control the complexity of programming. For example, when reading method [io]M[ix], the programmer needs only to be aware of the functionality of the preferred supplier classes of [io]M[ix]. The preferred suppliers of method [io]M[ix] are usually a small subset of all classes of the application and, furthermore, they are closely related to the class to which [io]M[ix] is attached. This relationship makes it easier for the programmer to remember those classes and their functionality. |au: The next paragraph says that you have been presenting the class version of the law enforceable at compile time; but that was not said until this point. It needs to be said before you present it, not after. However, you did have a transition here that helped explain the shift in topic. This is the kind of glue that the article needs throughout in addition to the reorganization. Also, check sentences 1 and 2; I added "at the class level" in sentence 1 to differentiate enforcement at the two levels/forms, and I added "basic" in "spirit of the basic law" to differentiate the object form from the law as a whole. Correct?| [bo]Object form.[bx] It is easy to extend a C++ compiler to check for the strict form of the Law of Demeter, but the price you pay for compile-time enforceability is that some programs that violate the spirit of the law will pass the test or that some programs that follow the spirit of the law will be rejected. The object version of the law -- which says all methods may only have preferred supplier objects -- expresses the spirit of the basic law and serves as a conceptual guideline for you to approximate in programming. |au: I assumed that the "first step" referred to extending a C++ compiler at the object level, since it's looking a supplier objects. Correct?| Preferred supplier objects are defined in analogy to preferred supplier classes. A supplier object of a method [io]M[ix] is an object to which a message is sent in [io]M[ix]. The preferred supplier objects of method [io]M[ix] are [cb] the immediate parts of self, [cb] the argument objects of M, or [cb] the objects that are either objects created directly in [io]M[ix] or objects in global variables. |au: Check sentence 1.| The programmer determines the granularity of the "immediate subparts of Self" on the application at hand. For example, the immediate parts of a list class are the elements of the list. The immediate parts of a regular class object are the objects stored in instance variables. |au: In sentence 1, shouldn't it be "While the class version ..." ? Also, you have not been relating the law to good style, so sentence 2 stands weakly. (You do relate them later, but that doesn't help the reader here, where you first raise the issue.)| While the object version of the law expresses what is really wanted, it cannot be enforced at compile time. The object version serves as an additional guide in addition to the class version of the law. %|au: You have given no examples of this form. At least explain %the reason to minimize the number of acquaintance classes.| % [bo]Minimization form.[bx] The most permissive form of the %Law of Demeter is the minimization form, which says to minimize %the number of acquaintance classes over all methods. Limiting the %number of acquaintance classes xxxxxx. [hs1]Principles |au: I still don't know how the law reduces the occurrences of function calls. Simply by limiting message passing, dependencies, and collaboration? And define what you mean by "certain."| The motivation behind the Law of Demeter is to ensure that the software is as modular as possible. The law effectively reduces the occurrences of certain nested message sendings (function calls) and simplifies the methods. The Law of Demeter has many implications for widely known software-engineering principles. Our contribution is to condense many of the proven principles of software design into a single statement that can easily be used by the object-oriented programmer and that can be easily checked at compile time. Principles covered by the law include: [cb] Coupling control. It is a well-known principle of software design to have minimal coupling between abstractions (like procedures, modules, and methods). The coupling can be along several links. An important link for methods is the Uses link (or call/return link) that is established when one method calls another. The Law of Demeter effectively reduces the methods you can call inside a given method and therefore limits the coupling of methods for the Uses relation. The law therefore facilitates reusability of methods and lifts the software's abstraction level. [cb] Information hiding. The Law of Demeter enforces one kind of information hiding: structure hiding. The law generally prevents a method from directly retrieving a subpart of an object that lies deep in that object's Part-of hierarchy. Instead, you must use intermediate methods to traverse the Part-of hierarchy in controlled, small steps.[fo]7[fx] In some object-oriented systems, the user can protect some of the instance variables or methods of a class from outside access by making them private. This important feature complements the law to increase modularity. But the law benefits even those system with privacy: It promotes the idea that the instance variables and methods that are public should be used in a restricted way. [cb] Information restriction. Our work is related to the work by David Parnas and colleagues[fo]8[fx] on the modular structure of complex systems. To reduce the cost of software changes in their operational flight program for the A-7E aircraft, they restricted the use of modules that provide information subject to change. We take this point of view seriously in our object-oriented programming and assume that any class could change. Therefore, we restrict the use of message sendings (function calls) by applying the Law of Demeter. Information restriction complements information hiding: Instead of hiding certain methods, you make them public but you restrict their use. [cb] Information localization. Many software-engineering textbooks stress the importance of localizing information, and the Law of Demeter focuses on localizing type information. When you study a method, you have only to be aware of types that are very closely related to the class to which the method is attached. You can effectively be ignorant (and independent) of the rest of the system. As the saying goes, "Ignorance is bliss." This important aspect of the law helps to reduce the complexity of programming. The law also controls how visible message names are: In a method, you can use only message names that are in the signatures of the instance variable types and argument types. This too localizes information. |au: "Facilitates structural induction proofs" is a bit obscure, at least to me. How many readers will understand this?| [cb] Structural induction. The Law of Demeter is related to the fundamental thesis of denotational semantics: The meaning of a phrase is a function of the meanings of its immediate constituents. This goes back to Frege's work on the principle of compositionality,[fo]9[fx] which facilitates structural induction proofs for program properties such as correctness with respect to a specification. [hs1]Example |au: Explain what you are showing the reader before jumping into the example. Don't assume the reader knows what you intend to do before you do, and don't assume the reader will spend the energy to figure out what you are doing. Be explicit about your intent. You need a transition to tie in the class dictionary example to the C++ program. Is it a C++ implementation of this class dictionary? Also, I moved the next two long code segments into their own figures.| Next we consider a program which violates the strict and the minimization form of the Law of Demeter and then we rewrite it to conform to both forms of the law. For this example, we use the classes defined by the class-dictionary fragment for a library in Figure 2[fa2]. We write a C++ program for searching a reference section for a book. We omit the class definitions from the C++ program (the Demeter system generates them automatically from the class dictionary). In C++, sending a message means calling a (virtual) member function. In the C++ examples which we use the types of data members and function arguments are pointer types to classes. We use a terminology to explain the C++ program which can also be understood by Smalltalk and Flavors users. Consider the fragment of a C++ program in Figure 3[fa3] that searches the reference section for a book. (To keep the example small, we used direct access to instance variables instead of using access methods.) The search_bad_style function attached to ReferenceSec passes the message to its book (BooksSec), microfiche (MicroFicheFiles), and document sections (Documents). |au: [am] is the keyboard asterisk, which is sized and positioned differently than the typographic asterisk.| This function breaks the Law of Demeter. The first message marked /[am][am]/ sends the message arch_microfiche to archive, which returns an object of type MicroficheFiles. The method next sends this returned object the search message. However, MicroficheFiles is not an instance variable or argument type of class ReferenceSec. |au: Note my addition at the end of sentence 1 to make it clear why you are changing the class dictionary (which I gather was to show that a violation of the law that works in one instance would not work in other instances).| Because the structure of all the classes are clearly defined by the class dictionary, you might be tempted to accept the methods in Figure 3 as a reasonable solution, even though they violate the Law of Demeter. But consider a change to the class dictionary. Assume the library installs new technology and replaces the microfiche and document sections of the archive with CD-ROMs or videodiscs: [mo]class Archive has parts cd_rom_arch : CD_ROM_File end class Archive. class CD_ROM_File has parts cd_c_system : ComputerSystem discs : CD_ROM_Discs end class CD_ROM_File. [mx]You now have to search [io]all[ix] the methods, including the search_bad_style method, for references to an archive with microfiche files. It would be easier to limit the modifications only to those methods attached to class Archive. You accomplish this by rewriting the methods in good style, which results in search_good_style functions attached to ReferenceSec and Archive. Using good style also reduces the coupling for the Uses relation: In the original version, ReferenceSec was coupled with BooksSec, Archive, MicroficheFiles, and Documents, but it now is coupled only with BooksSec and Archive. |au: Figures 4a and 4b are original Figures 2 and 3, respectively.| Another way to examine the effects of using the Law of Demeter is to translate a program -- in both good and bad style - - into a dependency graph. In the graphs, the nodes are classes. An edge from class [io]A[ix] to class [io]B[ix] has an integer label that indicates how many calls that [io]A[ix]'s functions make to class [io]B[ix]'s. If a label is omitted from an edge, its value is 1. Access to an instance variable is interpreted as a call to read the instance variable. Figure 4a[fa4] shows the graph for the program that violates the Law of Demeter; Figure 4b shows the graph for the one that follows the law. % Sometimes, following the Law of Demeter does not make it %easier to modify a program, but it does help you be more aware of %issues when writing a program in the first place. For example, %consider a program written in bad style. In Flavors, it is %[mo](defmethod (C :M) () % (send (send a :m1) :m2)) %[mx]In C++, it is %[mo]void C::M() % {a [nd]> m1() [nd]> m2();} %[mx]where [io]a[ix] is an instance variable of [io]C[ix] and %[io]m1[ix] and [io]m2[ix] are user-defined methods (not instance- %variable access methods). If you assume that [io]m1[ix] does not %return an object that is of an instance-variable type of %[io]C[ix], the nested send expression is in bad style. Being in %bad style makes it neither easier nor harder to modify the %methods when the class dictionary changes. But it does require %the programmer to think about other types than the instance- %variable types of [io]C[ix]. % You can rewrite these programs in good style and remove the %burden of thinking about other types from the programmer. In %Flavors, it would be %[mo](defmethod (C :M) () % (send self :m3 (send a :m1))) % (defmethod (C :m3) (arg :type Argtype) % (send arg :m2)) %[mx]In C++, it would be %|au: [el] is the typographic ellipsis.| %[mo]void C::M() % {this [nd]> m3(a [nd]> m1()); // [el] } % void C::m3(Argtype[am] arg) % {arg [nd]> m2(); // [el] } %[mx]Here the additional type is made explicit as an argument %type. Sometimes, you can rewrite a program of this form without %introducing additional arguments. [hs1]Valid use of non-preferred acquaintance classes |au: How does the law provide documentation? Do you mean that if you obey the law and the maintainers know it that they can easily decompose your meaning because you are all using the same assumptions and style? Or do you mean that the Demeter system automatically documents code that follows the style (if so, remember that this article is not about Demeter but about using the law in any object-oriented language).| The strict form of the Law of Demeter is intended to be a guideline, not an absolute restriction. The minimization form of the Law of Demeter allows the programmer a choice how strongly he or she wants to follow the strict Law; the more non-preferred acquaintance classes are used the weaker is the adherance to the strict Law). In some situations, the cost of obeying the strict law may be greater than the benefits. When you willingly violate the law, you take on the responsibility of declaring the required acquaintance classes. This is useful documentation for future maintainers of your software. |au: Note my transition.| As an example of where the cost of applying the law is higher than its benefits, consider the following prototypical method, which is in bad style. In Flavors, it is [mo](defmethod (C :M) (p) ( [el]. (send (send p :F1) :F2) [el].)) [mx]In C++, it is [mo]void C::M(D[am] p) {p [nd]> F1() [nd]> F2(); // [el] } [mx]where [io]p[ix] is an instance of class [io]A[ix] and [io]F1[ix] returns a subpart of [io]p[ix]. If the immediate composition of [io]A[ix] changes the method, [io]M[ix] may also have to change because of [io]F1[ix]. Here is a situation when it is reasonable to leave this bad-style code as it is: [cb] [io]F1[ix] is intended to serve as a black box, and the programmer knows only about the types of its arguments and the return type. In this case, the maintainer of [io]F1[ix] must ensure that any updates to [io]F1[ix] are upwardly compatible so users of the function are not penalized for using it. %|au: The implication here is that friend functions are inherently %bad style. If so, say so and explain why. If not, explain how %using a friend function violates the law (and why it is a good %reason to violate the law).| % [cb] If runtime efficiency is important, using mechanisms %like C++'s friend functions may be necessary. Friend functions %should be used carefully because whenever the private members of %a class change, the friend functions of the class might also %change. |au: Note my transition. Verify that the first code fragment is from the class library (the original did not say what kind of code this was).| Consider another example that shows where the costs of using the law might outweigh its benefits. For an application that solves differential equations, the class library may have the following definitions: [mo]class Complex_Number has parts real_part : Real imaginary_part : Real end class Complex_Number. [mx]In Flavors, some code using these definitions would be [mo](defmethod (Vector :R) (c :Complex_Number) ( [el].. (send (send c :real_part) :project self) [el].)) The same code in C++ is: [mo]void Vector::R(Complex_Number[am] c) { c [nd]> real_part [nd]> project(this) } [mx]The method [io]R[ix] is in the same form as [io]M[ix] in the previous example and is in bad style for the same reason. The question here is whether it is important to hide the structure of complex numbers and to rewrite the method. In this application, where the concept of a complex number is well defined and well understood, it is unnecessary to rewrite the method so that the law is obeyed. In general, if the application concepts are well defined and the classes that implement those concepts are stable, such violations are acceptable. Writing programs that follow the Law of Demeter decreases the occurrences of nested message sending and decreases the complexity of the methods, but it increases the number of methods. The increase in methods is related to the problem that there can be too many operations in a type.[fo]7[fx] In this case, the abstraction may be less comprehensible, and implementation and maintenance more difficult, if you apply the law. There might also be an increase in the number of arguments passed to some methods. % One way to correct this problem is to organize all the %methods associated with a functional (or algorithmic) task into %Modula-2-like module structures[fo]10[fx] so the functional %abstraction is no longer a method but a module that will hide the %lower level methods that caused the original confusion. [hs1]Conformance |au: In sentence 3, what weak law? This is your first mention of it. Explain what it means. My boss thinks you mean minimization form. Correct?| Given a method that does not satisfy the law, how can you transform it so that it conforms to the law? In an earlier paper,[fo]4[fx] we described an algorithm to transform any object-oriented program into an equivalent program that satisfies the strict law. We showed that you can translate any object-oriented program into a form that satisfies the strict law. There are other -- but less automatic -- ways to achieve this goal that may help you derive more readable or intuitive code. They also may help you minimize the number of arguments passed to methods and the amount of code duplication. Two such techniques are called lifting and pushing. Using the following recursive definition, we will use these techniques to transform programs into forms that satisfy the Law of Demeter. The definition is that [io]B[ix] is a part class of [io]A[ix] if [io]B[ix] is an instance-variable class of [io]A[ix] or if [io]B[ix] is a part class of an instance-variable class of [io]A[ix]. Consider the following program which violates the strict law. In Flavors, the method is [mo](defmethod (C :M) () (send (send self ':m1) ':m2)) [mx]while in C++ it is [mo]void C::M() {this [nd]> m1() [nd]> m2();} [mx]and T is the class of the object returned by m1. T is not a preferred supplier class of M. We distinguish two cases: |au: You mention three cases but only talk about transforming two of them. Give an example for case 3 or explain why you aren't.| [cb] [io]T[ix] is a part class of [io]C[ix]. [cb] [io]C[ix] is a part class of [io]T[ix]. [bo]Lifting. This technique is applicable in case 1 (if [io]T[ix] is a part class of [io]C[ix]). The idea is to make [io]m1[ix] return an object of an instance-variable or argument class of [io]C[ix] and to adjust [io]m2[ix] accordingly. Method [io]m2[ix] is lifted up in the class hierarchy, from being attached to class [io]T[ix] to being attached to an instance- variable class of [io]C[ix]. |au: Figure 5 is the three segments from the grammar example plus the original Figure 4.| For example, suppose you want to parse an input using some grammar. A grammar is made up of a list of rules such as that in Figure 5.[fa5] This program fragment uses one acquaintance class (class Body in the method parse for Grammar) and is represented by Figure 5b and 5c |au: Figure 6 is the two segments from the revised grammar example plus the original Figure 5.| The problem with the fragment is that method look_up of Grammar returns an object of type Body that is not an instance- variable type of Grammar. To transform the first method into good style, you must make the look_up method return an instance of Rule and then you must adjust parse_details. Figure 6[fa6] shows this modified version. This improved program fragment uses no acquaintance class. |au: Figure 7 is the three segments from the revised grammar example plus the original Figure 6.| But this lifting approach does not always work. Consider Figure 7.[fa7] This program fragment uses one acquaintance class (class Rule in method parse of Grammar). Here, you cannot transform the first method into good style by lifting the return type of the look_up method. Pushing. This technique is applicable in cases 1 and 2 ([io]T[ix] is a part class of [io]C[ix] and [io]C[ix] is a part class of [io]T[ix], respectively). (Case 2 is slightly more complicated because it involves traveling up the object hierarchy, but the general technique is the same as for case 1.) It is just a variation of the top-down programming technique of pushing the responsibility for doing the work to a lower level procedure. |au: Figure 8 is the two segments from the revised grammar example plus the original Figure 7.| In the lifting example, a problem arose because the Grammar class has the task of sending the parse_details message. This task is really the responsibility of RuleList, which knows more about Rule details than Grammar. Figure 8[fa8] shows an improved design that does not use any acquaintance classes. The redesign has introduced an additional method. If you view list classes as stable (for example, as is true in Smalltalk), there is no need for the redesign and it is justified to keep the acquaintance class. |au: Our conclusions are not summaries, so I removed repetitive material. Feel free to add material that ties the article together and shows directions the reader or you might now take.| [ks1]The style of modular programming encouraged by the Law of Demeter leads naturally to code which is easier to understand and maintain. The law lets the programmer redesign classes (even their interfaces) while leaving more of the existing software intact. This level of flexibility is necessary to achieve a high rate of modification. Furthermore, effectively reducing the effects of local changes to a software system can reduce many of the headaches of software maintenance. But following the law exacts a price. The greater the level of interface restriction (a refinement of hiding), the greater the penalties are in terms of the number of methods, execution speed, number of arguments to methods, and sometimes code readability. In the long term, however, these prices are not fatal penalties. The support of an interactive CASE environment can erase some of the penalties of following the law. You can counter the execution speed problem by using preprocessor or compiler technologies like in- line code expansion or code optimization similar to how tail- recursion optimization is done. In the past year, more than a hundred students scrutinized, challenged, and tested the Law of Demeter. We have used it in the ongoing development of our Demeter system without ever being prevented from achieving our algorithmic goals (although we had to rewrite some methods). We are continuing our investigation of modular object-oriented programming and believe that the law leads to the development of good software when used in conjunction with other well-known style rules. [db] [hs2]Acknowledgments [ts2] A version of this article appeared in reference 11 [io]Proceedings of the 1988 Object-Oriented Programming Systems, Languages, and Architectures Conference[ix]. The June 1988 [io]Computer[ix] had a short piece in its Open Channel section that cursorily described the law. |au: Reconsider the length of these thanks. It is unusually long. One way would be to thank the people without detailing their contributions to such length. Whether and how to do this is your decision.| Carl Woolf suggested that the object version of the law is the one to be followed conceptually. Special thanks are due to Arthur Riel, who was a principal author on earlier versions of this article. Members of the CLOS community (including Daniel Bobrow, Richard Gabriel, Jim Kempf, Gregor Kiczales, and Alan Snyder) have participated in the debate and form of the CLOS version of the law. We thank Markku Sakkinen for reference 5 and for his helpful mail messages about the law of Demeter. Cindy Brown and Mitch Wand convinced us that we should use a more readable notation than extended Backus- Naur form and they helped us design it. Paul Steckler and Ignacio Silva-Lepe made several contributions to the extended Demeter notation. |au: Supply missing months information.| [hs2]References [ts2]<>1.<>T. Kaehler and D. Patterson, [io]A Taste of Smalltalk[ix], W.W. Norton, New York, 1986. <>2.<>A. Snyder, "Inheritance and the Development of Encapsulated Software Systems," in [io]Research Directions in Object-Oriented Programming[ix], B. Shriver and P. Wegner, eds., MIT Press, Cambridge, MA 1987, pp. 147-164. <>3.<>K.J. Lieberherr, "Object-Oriented Programming with Class Dictionaries," [io]J. Lisp and Symbolic Computation[ix], Volume 1, Number 2, 1988, pp. 185-212. <>4.<>K.J. Lieberherr and I. Holland, "Formulations and Benefits of the Law of Demeter," [io]SIGPlan Notices[ix], March 1989, pp. 67- 78. <>5.<>M. Sakkinen, "Comments on the Law of Demeter and C++," [io]SIGPlan Notices[ix], December 1988, pp. 38-44. <>6.<>C. Hewitt and H. Baker, "Laws for Communicating Parallel Processes, [io]Proc. IFIP World Congress[ix], Int'l Fed. Information Processing, Geneva, 1977. <>7.<>B. Liskov and J. Guttag, [io]Abstraction and Specification in Program Development[ix], MIT Press, Cambridge, Mass., 1986. <>8.<>D.L. Parnas, P.C. Clements, and D.M. Weiss, "The Modular Structure of Complex Systems," [io]IEEE Trans. Software Eng.[ix], month 1985, pp. 259-266. <>9.<>J.V. Heijenoort, [io]From Frege to G[au]odel[ix], Harvard University Press, Cambridge, Mass., 1963. |au: The original had "CASE." Was this a pun based on a case study involving CASE? If so, I'll put it back as "CASE."| |kl: yes| 10.<>K.J. Lieberherr and A.J. Riel, "Demeter: A CASE Study of Software Growth through Parameterized Classes," [io]J. Object- Oriented Programming[ix], August/September 1988, pp. 8-22. |kl: need OOPSLA reference elsewhere| 11. OOPSLA'88 paper: Objective sense of style [bb][bo]Karl J. Lieberherr[bx] is a professor of computer science at Northeastern University. He leads the team developing the Demeter system, which consists of grammar-based tools for object- oriented programming in C++, extensions of Lisp, and other class- based object-oriented languages. He has also been a principal technical staff member at GTE Laboratories and assistant professor of computer science at Princeton University. His current research interests include object-oriented programming systems. Lieberherr received his Ph.D. in mathematics/computer science from the Swiss Federal Institute of Technology (ETH) in Zurich in 1977. He is a member of IEEE, ACM and AAAI. [bb][bo]Ian Holland[bx] is a PhD candidate in computer science at Northeastern University. He has worked in Ireland and West Germany in commercial computer science. His research interests include object-oriented programming and CASE. |au: Were both degrees in both fields?| Holland received a BS and MS in computer science and mathematics from University College in Cork, Ireland. He is a student member of IEEE and ACM. Address questions to the authors at Computer Science College, 161 Cullinane Hall, Northeastern University, 360 Huntington Ave., Boston, MA 02115; CSnet lieber@corwin.ccs.northeastern.edu. sidebar XX [hs3]The Demeter(TM) system |au: In sentence 1, what is this important part of the development process?| |au: You had said "Old Flavors" below. I've never heard of it. Is plain Flavors wrong?| [ts3] Demeter's key contribution is to improve programmer productivity by several factors for an important part of the development process: the preparation of a personalized software library for working with the objects defined by the classes. The key ideas behind the Demeter system are \bi \item to use a more expressive class notation than in existing object-oriented programming languages and \item to take advantage of the expressiveness by providing a large number of custom-made utilities similar to a personalized library. These utilities are provided in a specific object-oriented language, e.g., C++ or Flavors, and impressively simplify the programming task. \ei |au: The next sentence didn't say what generated or applied the utilities; I assumed it was Demeter. Correct?| Examples of utilities Demeter generates or applies generically are class definitions in a language, application skeletons, parsers, pretty printers, type checkers, object editors, recompilation minimizers, pattern matchers, and unifiers. The Demeter system helps the user define the classes (both their structure and the high levels of their functionality) with several support tools, including a consistency checker (semantic rules and type checking at the design level), a learning tool that learns class definitions from example object descriptions, an LL(1) corrector for left-to-right scans with one look-ahead token producing a leftmost derivation, script generator based on wish lists, and an application-development plan generator. One of Demeter's primary goals is to develop an environment that eases the evolution of a class hierarchy. Such an environment must provide tools for the easy updating of existing software (the methods or operations defined on the class hierarchy). We are striving to produce an environment that will let software to be grown in a continuous fashion rather than in the sporadic jumps that undoubtedly lead to major rewrites. We believe a continuous-growth environment will lead to the fast- prototyping/system-updating development cycle common in the artificial-intelligence community. |au: Check my definition of EBNF. If the "E" does indeed stand for "extended," the use of extended for both notations could be confusing. I tried to differentiate the two (see the next paragraph). If it means something else, spell it out. Also, you might define "syntactic `sugar'" -- I realize it's a common term in some communities, but I suspect from the usages that I hear that its definition is not firm.| [bo]Class definitions.[bx] Demeter describes classes with three kinds of class definitions: construction, alternation, and repetition. A collection of these class definitions is called a class dictionary. The class dictionary below partially defines a reference section of a library: [mo]class ReferenceSec has parts ref_book_sec : BooksSec archive : Archive end class ReferenceSec. class Archive has parts arch_microfiche : MicroficheFiles arch_docs : Documents end class Archive. class BooksSec has parts ref_books : ListofBooks ref_catalog : Catalog end class BooksSec. class ListofBooks is list repeat {Book} end class ListofBooks. class Catalog is list repeat {Catalog_Entry} end class Catalog. class Book has parts title : String author : String id : BookIdentifier end class Book. [mx] A construction-class definition is used to build a class from several other classes and has the form [mo]class C has parts part_name_1 : SC_1 part_name_2 : SC_2 [el] part_name_n : SC_n end class C. [mx]An object of class C, [io]C[ix] is defined as being made up of [io]n[ix] parts (called its instance-variable values), and each part has a name (called an instance-variable name) followed by a type (called an instance-variable type). This means that for any instance (or member) of class [io]C[ix] the name part_name_i refers to a member of class SC_i. The following example describes a library class consisting of a reference section, a loan section, and a journal section. [mo]class Library has parts reference : ReferenceSec loan : LoanSec journal : JournalSec end class Library. [mx]We use the following naming convention: Instance variable names begin with a lowercase letter and class names begin with an uppercase letter. An alternation-class definition lets you express a union type. A class definition of the form [mo]class C is either A or B end class C. [mx]states that a member of [io]C[ix] is a member of class [io]A[ix] or class [io]B[ix] (exclusively). For example, [mo]class Book_Identifier is either ISBN or LibraryOfCongress end class Book_Identifier. [mx]expresses the notion that when you refer to the identifier of a book you are actually referring to its ISBN code or its Library of Congress code. A repetition-class definition is simply a variation of the construction-class definition where all the instance variables have the same type and you do not specify the number of instance variables involved. The class definition [mo]class C is list repeat {A} end class C. [mx]defines members of [io]C[ix] to be lists of zero or more instances of [io]A[ix]. [bo]Notation.[bx] We use two notations in the Demeter system: A concise notation based on the concise extended Backus- Naur forms and an extended notation of our own that is largely self-explanatory. In this article, we use our extended notation. The abstract syntax of the concise and extended notations is identical: Only the syntactic "sugar" is changed. sidebar YY [hs3]Forms for popular languages |au: I wrote an introduction to this sidebar. Feel free to rewrite it. Also, if there is a logical place to refer to this sidebar from the main text, please do so.| [ts3] To use the Law of Demeter effectively, you must customize it to your language. Here are forms we have derived for several popular object-oriented languages. For C++ we give the class form (strict law) and for the other languages we give the object form of the law. This choice is arbitrary but for the statically typed languages C++ and Eiffel, the class form is most useful since it can be checked by a modified compiler. The Eiffel users will have little difficulty in formulating the class form. |au: Consider explaining why for some langauges you give class versions and for others you give object versions. Also, perhaps we should replace "version" with "form" to be consistent with the rest of the article.| [bo]C++, strict form.[bx] In all member functions [io]M[ix] of class [io]C[ix], you may use only members (function and data) of the following classes and their base classes: [cb] [io]C[ix], [cb] data-member classes of [io]C[ix], [cb] argument classes of [io]M[ix], or [cb] special acquaintance classes of [io]M[ix] that are either classes whose constructor functions are called in [io]M[ix] or the classes of global variables used in [io]M[ix]. [bo]CLOS, object form.[bx] We assume that the user can determine for each generic function the number of method- selection arguments (not necessarily all required ones) and that this number is part of the interface of the generic function. A method-selection argument is an argument used to identify the applicable methods. All function calls inside method [io]M[ix] must use only the following objects as method selection arguments: [cb] [io]M[ix]'s argument objects, [cb] immediate parts of method-selection arguments of [io]M[ix], or [cb] an object that is either an object created directly by [io]M[ix] or an object in a global variable. This form implies that the only legal use of the slot-value function is on method-selection arguments. [bo]Eiffel, object form.[bx] In all calls of routines inside routine [io]M[ix], the entity object must be one of the following objects: [cb] an argument object of [io]M[ix], [cb] an attribute object of the class in which [io]M[ix] is defined, or [cb] an object created directly by [io]M[ix]. [bo]Flavors, object form.[bx] In any method [io]M[ix] attached to class [io]C[ix] you may send messages only to the following objects: [cb] [io]M[ix]'s argument objects, [cb] the instance variable objects of [io]C[ix], or [cb] an object that is either an object created directly by [io]M[ix] or an object in a global variable. [bo]Smalltalk-80, object form.[bx] In all message expressions inside method [io]M[ix] the receiver must be one of the following objects: [cb] an argument object of [io]M[ix], including objects in pseudovariables Self and Super, [cb] an immediate part of Self, or [cb] an object that is either an object created directly by [io]M[ix] or an object in a global variable. |au: Please read through the captions and revise them as necessary to explain the figures.| [cs1]Figure 1.<> Example of client and provider definitions. [ts4];instance variable class: class C has parts s : B implements interface M() returns Ident {calls s [nd]> f()} end class C. ;argument class: class C has parts ; none implements interface M(s : B) returns Ident {calls s [nd]> f()} end class C. ;argument class: class B has parts ; none implements interface M() returns Ident {calls self [nd]> f()} ; in C++, Self is called This end class C. ;newly created object class: class C has parts ; none implements interface M() returns Ident ; new_object is new B-object {calls new_object [nd]> f()} end class C. ; s is of type B, global class C has parts ; none implements interface M() returns Ident {calls s [nd]> f()} end class C. [cs1]Figure 2.<> Class-dictionary fragment for a library. [ts4]class Library has parts reference : ReferenceSec loan : LoanSec journal : JournalSec end class Library. class ReferenceSec has parts ref_book_sec : BooksSec archive : Archive end class ReferenceSec. class Archive has parts arch_microfiche : MicroficheFiles arch_docs : Documents end class Archive. class MicroficheFiles has parts [el] end class MicroficheFiles. class Documents has parts [el] end class Documents. class BooksSec has parts [el] end class BooksSec. [cs1]Figure 3.<> A C++ fragment to search the reference section for a book. Function search_bad_style violates the Law of Demeter. [ts4]class ReferenceSec { public: Archive[am] archive; BookSec[am] ref_book_sec; boolean search_bad_style(Book[am] book) { return ( ref_book_sec[nd]>search(book) || /[am][am]/ archive[nd]>arch_microfiche[nd]>search(book) || /[am][am]/ archive[nd]>arch_docs[nd]>search(book)); } boolean search_good_style(Book[am] book) { return archive[nd]>search_good_style(book); } }; class Archive { public: MicroFicheFiles[am] arch_microfiche; Documents[am] arch_docs; boolean search_good_style(Book[am] book) { return (arch_microfiche[nd]>search(book) || arch_docs[nd]>search(book)); } }; class MicroFicheFiles { public: boolean search(Book[am] book) {} }; class Documents { public: boolean search(Book[am] book) {} }; class Book { [el]. }; [cs1]Figure 4. <> Dependency graph representations of <>(a)<> the bad-style code in Figure 3 and the <>(b)<> good-style code. The numbers labeling the edges between classes indicate the number of function calls from one class to functions of another class. [cs1]Figure 5.<> Example code that violates the Law of Demeter: <>(a)<> class dictionary, <>(b)<> Flavors code, <>(c)<> C++ code, and <>(d)<> its dependency graph. [ts4]; class Grammar is list ; repeat {Rule} ; end class Grammar. ; class Rule has parts ; body : Body ; end class Rule. (a) (defmethod (Grammar :parse) (rule_name :type Symbol) (send (send self ':look_up rule_name) ':parse_details)) (defmethod (Grammar :look_up) (rule_name :type Symbol) [el] (send (send rule ':look_up rule_name) ':get_body)) (defmethod (Body :parse_details) () [el] ) (b) void Grammar::parse(Symbol[am] rule_name) {this [nd]> look_up(rule_name) [nd]> parse_details();} Body[am] Grammar::look_up(Symbol[am] rule_name) {[el] return rule [nd]> look_up(rule_name) [nd]> get_body(); } void Body::parse_details() { [el] } (c) (d) [cs1]Figure 6.<> Version of the Figure 5 example code transformed with the lifting technique to conform to the Law of Demeter: <>(a)<> Flavors code, <>(b)<> C++ code, and <>(c)<> its dependency graph. [ts4](defmethod (Grammar :parse) (rule_name :type Symbol) (send (send self ':look_up rule_name) ':parse_details)) (defmethod (Grammar :look_up) (rule_name :type Symbol) [el] (send rule ':look_up rule_name)) (defmethod (Rule :parse_details) () ..(send self ':get_body) [el] ) (a) void Grammar::look_up(Symbol[am] rule_name) {this [nd]> look_up(rule_name) [nd]> parse_details();} Rule[am] Grammar::look_up(Symbol[am] rule_name) {[el] return rule [nd]> look_up(rule_name);} void Rule::parse_details() {[el] this [nd]> get_body(); [el]} (b) (c) [cs1]Figure 7.<> Example code that violates the Law of Demeter and that cannot be fixed with the lifting technique: <>(a)<> class dictionary, <>(b)<> Flavors code, <>(c)<> C++ code, and <>(d)<> its dependency graph. [ts4]; class Grammar has parts ; ruleList : RuleList ; end class Grammar. ; class RuleList is list ; repeat {Rule} ; end class RuleList. ; class Rule has parts ; rule : Rule ; end class Rule. (a) (defmethod (Grammar :parse) (rule_name :type Symbol) (send (send-self ':look_up rule_name) ':parse_details)) (defmethod (Grammar :look_up) (rule_name :type Symbol) ; returns object of type Rule [el] (send ruleList ':look_up rule_name)) (defmethod (RuleList :look_up) (rule_name :type Symbol) [el] ) (defmethod (Rule :parse_details) () [el] ) (b) void Grammar::parse(Symbol[am] rule_name) {this [nd]> look_up(rule_name) [nd]> parse_details();} Rule[am] Grammar::look_up(Symbol[am] rule_name) { [el] ruleList [nd]> look_up(rule_name); } void RuleList::look_up(Symbol[am] rule_name) { [el] } void Rule::parse_details() { [el] } (c) (d) [cs1]Figure 8.<> Version of the Figure 7 example code transformed with the pushing technique to conform to the Law of Demeter: <>(a)<> Flavors code, <>(b)<> C++ code, and <>(c)<> its dependency graph. [ts4](defmethod (Grammar :parse) (rule_name) (send self ':look_up_parse rule_name)) (defmethod (Grammar :look_up_parse) (rule_name) (send ruleList ':look_up_parse rule_name)) (defmethod (RuleList :look_up_parse) (rule_name) (send (send-self ':look_up rule_name) ':parse_details)) (a) void Grammar::parse(Symbol[am] rule_name) {this [nd]> look_up_parse(rule_name);} void Grammar::look_up_parse(Symbol[am] rule_name) {ruleList [nd]> look_up_parse(rule_name);} void RuleList::look_up_parse(Symbol[am] rule_name) {this [nd]> look_up(rule_name) [nd]> parse_details();} (b) (c)