Re: Opinions on the Law Of Demeter

From: H. S. Lahman (h.lahman_at_verizon.net)
Date: 10/28/04


Date: Thu, 28 Oct 2004 16:30:52 GMT

Responding to Daniel T....

> Because of your comment, I decided to go to the source
> <ftp://ftp.ccs.neu.edu> (in
> pub/demeter/documents/papers/LH89-law-of-demeter.ps) and sure enough, it
> coincides with what I have above:
>
> """
> A supplier object to a method M is an object to which a message is sent
> in M. The preferred supplier objects to method M are:
> * the immediate parts of self or
> * the argument objects of M or
> * the objects which are either objects created directly in M or
> objects in global variables.
> """
> [Assuring Good Style for Object-Oriented Programs - Karl J Lieberherr
> and Ian Holland - pg 5]

OK, but...

>>That form of relationship instantiation is very poor
>>OO practice. (The exception, of course, are knowledge setters that
>>instantiate a relationship as a referential attribute.)
>
>
> Obviously, we disagree on this point. I think it helps tremendously in
> making sure that code is cohesive and loosely coupled. Please see the
> cited paper for the reasoning behind this assertion.

Unfortunately it is a pretty basic point for disagreement. B-) There
are several problems with passing object references in messages:

[Client] --------- [Supplier] ------------- [Other]
                    + M(Other*)

When Client passes a reference to the Other object to M it is defining a
temporary relationship between Supplier and Other. That relationship is
really a personal matter between Supplier and Other. That has nothing
to do with why Client needs to collaborate with Supplier so Client
should have no knowledge of it to collaborate with Supplier.

The only object that should have knowledge of it is whoever understands
the rules and policies of participation between Supplier and Other AND
understands the context when they apply. For an unconditional
relationship that is almost always whoever instantiates Supplier and
Other. The concerns of instantiation are usually different than the
concerns of collaboration, which is why Client should usually not know
anything about the relationship between Supplier and Other.

[Occasionally Client will also be the one responsible for instantiation
of the relationship (e.g., when Other needs to be substituted based on a
context that only Client logically would know about). But that is
pretty rare in practice. The important point is that the practice
forces the developer to think long enough about it to justify an exception.]

This separation of concerns is very important to OOA/D because it tends
to improve maintainability. Passing an object reference is inherently
the worst form of coupling because the side effects of the receiver's
use of it is essentially unlimited; the receiver can invoke any
behavior, modify any attribute, and even delete it. This allows a
maintainer to modify M is a way that trashes the client's collaboration
with Supplier.

Having M collaborate with Other may seem like the same thing, but that
is superficial. Because OOA/D behavior collaboration is based upon
messages, M's collaboration with Other is not hidden in M's
implementation; the messages are defined at a higher level of
abstraction than individual behaviors (e.g., a UML Interaction Diagram).
  In addition, the messages are announcements, the collaboration is
peer-to-peer (rather than through Client), and the behaviors are
self-contained and logically indivisible.

All these things conspire to provide much greater and more explicit
encapsulation in the application. But coordinating all those balls in
the air is not always easy. So it has a much better chance of working
properly if one methodologically has the discipline to separate the
concerns of instantiation (participation) from those of navigation
(collaboration). It is that practice that drives the developer to think
properly about both the initial construction and subsequent maintenance.

Basically there are three ways to implement relationships: searches
based in embedded identity; passing object references; and employing
referential attributes that are true references. The first is the
relational approach and is usually hopelessly inefficient in
applications. The second is essentially a procedural or functional
programming approach. It is a form of parametric polymorphism; OO
practice relegates parametric polymorphism to state variables (e.g.,
specification objects) and employs inclusion polymorphism for behavior
substitution. The third is the standard OO approach that OOA/D
essentially assumes for navigating collaborations.

Finally, a less esoteric reason. Navigating by referential attributes
allows a different form of safety. Since referential attributes are
knowledge properties, they are accessed synchronously in the OOA/D while
behaviors are accessed asynchronously. That ensures that referential
integrity is preserved; the critter one obtains from the navigation is
guaranteed to be timely. Because of method scoping that is relatively
easy to enforce during OOP even in concurrent applications. The same is
not true for references in messages data packets that may be out of date
by the time the message is actually processed. Enforcing referential
integrity for references in message data packets tends to be seriously
nasty during OOP if the application is not a pure synchronous
implementation.

Since at least OOA is independent of issues like concurrency, the
paradigm of using referential attributes for relationship navigation is
pretty much standardized. Every abstract action language I know of for
OOA/D uses that paradigm for relationships because it makes it easier to
enforce referential integrity during OOP if the implementation needs to
be asynchronous and/or concurrent to satisfy nonfictional requirements.

>>Note that your version of LoD completely eliminates the second form of
>>collaboration for simple associations since myA is not embedded in B and
>>it won't be passed to any of B's methods. Or are you using a very
>>liberal notion of "parameters passed into it"? B-)
>
>
> LoD (as defined by the authors of the 'law' not just "my version") would
> disallow the 'navigateToA' methods in both examples, but otherwise would
> not change the code examples at all. In both cases, the B class methods
> can send messages to the 'myA' object because 'myA' is one of "the
> immediate parts of self."

Then you are using the very liberal interpretation of "immediate parts
of self". B-) The instance of A being accessed is not a part of B; it
is a peer object relative to B that was created by someone else. The
only "part of self" is the referential attribute that /refers/ to it.

By the way, eliminating the navigateToA prevents the relationship
implementation from being independent of the semantics of B. That, in
turn, prevents the relationship implementation from being treated as an
aspect. That becomes more obvious when one considers the aspect-like
code produced by a full code generator:

          1 R1 1 1 R1 1
[Client] ------------------- [B] --------------------- [A]

class B {
private:
     Object* refR2;
public:
     B (Object* o) {refR2 = o;}
     void instantiateR2 (Object* o) {refR2 = o;}
     Object* navigateR2 () {return refR2;}
}

class Client {
private:
     Object* refR1;
public:
     Client (Object* o) {refR1 = 0;}
     void instantiateR1 (Object* o) {refR1 = o;}
     Object* navigateR1 () {return refR1;}
     void doIt()

void Client::doIt () {
     A* myA;
     ...
     myA = (A*) refR1->navigateR2();
     ... // do something involving sending a message to A
}

[Before you jump all over it... B-) The code generator knows how to do
the "(A*)" cast the same way that it knows to use xxxRn -- it looks at
the Class Diagram to see who is at the end of the relationship path.
While a code generator can do this sort of thing with absolute
correctness, people tend to screw up and they need all the crutches
provided by static typing and semantic naming conventions at the OOP
level. (They are also somewhat crippled by the procedural message
passing used by the OOPLs that prevents them from naming messages
separately from methods.)]

Note that the code generator can write the relationship implementation
code for both classes with without knowing anything about the semantics
of B -- it doesn't even need to know what class B is. That's not
possible unless the relationships are implemented as referential
attributes that are accessible as knowledge properties (i.e., navigateR1).

In addition -- getting back to my point above about coupling -- you will
note that if requirements change such that Client needs to send the
message to B (rather than A), who must do something before a message is
sent to a different A, then the only thing that changes in the Client
implementation is navigating only R1 rather than navigating R1 -> R2.
IOW, the scope of change to Client will be reduced by separating the
concerns of instantiation in most cases.

*************
There is nothing wrong with me that could
not be cured by a capful of Drano.

H. S. Lahman
hsl@pathfindermda.com
Pathfinder Solutions -- Put MDA to Work
http://www.pathfindermda.com
blog (under constr): http://pathfinderpeople.blogs.com/hslahman
(888)-OOA-PATH



Relevant Pages

  • Re: Opinions on the Law Of Demeter
    ... > temporary relationship between Supplier and Other. ... > to do with why Client needs to collaborate with Supplier so Client ... The concerns of instantiation are usually different than the ... but has abdicated that responsibility ...
    (comp.object)
  • Re: Troubles with ActiveX-EXE (Out-Of-Process Server)
    ... If all the client connections are terminated properly, and the server isn't ... continuing to hold references on its own objects, then the ActiveX EXE ... EXE's own Collection is keeping references to those objects, ...
    (microsoft.public.vb.enterprise)
  • Re: idl questions
    ... the client references the dll ... even though there's no direct references to that class in the exe? ... then everything compiles and runs fine ... that creates the tlb in the /bin file... ...
    (microsoft.public.vb.general.discussion)
  • Re: 5 access 2000 users and 1 AccessXP user
    ... Remove any references you don't need from your mdb. ... Once you have that sorted out, create another mde (assuming you removed some ... The remaining problem is for an XP Client ... > I should also mention that, before the XP Client running Office 2000 was ...
    (microsoft.public.access.conversion)
  • Re: control value
    ... Each supplier furnishes all the products, but with different prices from one ... And perhaps other fields to identify the client …. ... UnitPrice: __txtBox (with the Price for that product that the supplier is ... "Jeff Boyce" escreveu: ...
    (microsoft.public.access.gettingstarted)