[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Law of good style for CLOS
- To: commonloops.pa@Xerox.COM
- Subject: Law of good style for CLOS
- From: karl lieberherr <lieber@corwin.ccs.northeastern.edu>
- Date: Thu, 9 Jun 88 17:23:54 EDT
- Redistributed: commonloops.pa
All the feedback we got on the Law of good style for CLOS has
prompted the Demeter team to find the proper generalization
of our Flavors/Smalltalk/C++ Law to the CLOS Law.
We give here our solution and explain it on some of the examples
which you sent us. A pretty complete explanation will be in our
OOPSLA '88 paper. A copy will be sent to Daniel Bobrow (as he
requested).
----- LAW OF DEMETER (CLOS, object version) -------------
All function calls inside a method M must have
one of the following method selection argument objects:
- M's argument objects or
- slot values of method selection argument classes of M.
(Objects created by the method and objects in global
variables are viewed as being passed by arguments.
A method selection argument is an argument which is used
for identifying the applicable methods.)
----------------------------------------------------------
This formulation is a natural generalization of the Flavors/
Smalltalk/C++ Law where there is exactly one method selection
argument (the first one). Note that there is no restriction on
non-generic function calls: they have 0 method selection arguments.
The Law requires the programmer to be aware whether a function
is generic (reversing our earlier point of view in our reply
to Scott Fahlmann's message).
Richard Gabriel invented the following example:
>(defmethod hack-positions ((p1 position) (p2 position))
> (let ((p1-x (x-coord p1))
> (p1-y (y-coord p1))
> (p2-x (x-coord p2))
> (p2-y (y-coord p2)))
> (let ((rho1 (sqrt (+ (* p1-x p1-x) (* p1-y p1-y))))
> (theta1 (atan p1-y p1-x))
> (rho2 (sqrt (+ (* p2-x p2-x) (* p2-y p2-y))))
> (theta2 (atan p2-y p2-x)))
> (let* ((q (f rho2 theta2 rho1 theta1))
> (r (g q))
> (s (h r))
> (tt (i s))
> (u (j tt)))
> (k u)))))
>Is the call (x-coord p1) a violation? [It is not known whether it is a
>slot access or a function call.] Is the call (x-coord p2) a violation?
>[It is invoked on something besides the first argument to the method.]
NO VIOLATION since p1 and p2 are method selection arguments.
>Is the call to SQRT a violation? [Its first argument is a number, but is
>not related to the first argument to the method.]
NO VIOLATION since + returns a new object.
How about the call to ATAN?
>[Its first argument is related to the second argument to the method
>via a LET variable.]
A VIOLATION if we assume that the objects returned by (x-coord p1)
and (y-coord p1) are not parts of p1. Otherwise no violation.
>How about the call to f? [Its first argument is
>derived from something related to the second argument to the method.]
f is in GOOD STYLE since it gets all newly created objects.
>The call to g? [Its first argument is indirectly related to the arguments to
>the method.
There is not enough type information here to tell.
>Karl, if you could answer whether anything in HACK-POSITIONS violates the law
>and why, we could begin to think about the issue you're raising. If nothing
>here violates the law, could *you* provide a method that violates the law?
Here is a simple example.
;;; A = B.
(defclass A () ((B :type B)))
;;; B = C.
(defclass B () ((C :type C)))
;;; C = D.
(defclass C () ((D :type D)))
(defmethod violation ((p1 A) (p2 Z))
(slot-value (slot-value (slot-value p1 'b) 'c)))
The idea is that it is a bad strategy to put a dependency on the
C class into this method which is "attached to" A and Z.
---------------------------------
Jim Kempf's remark promotes further encapsulation. It enhances the
Law but does not replace it.
>If a class is defined with accessor generic functions whose names are
>exported from the package, but are different from the slot names,
>which are not exported (or "hidden"), then the only access to the
>slots is through a functional interface. From the client's point of
>view, invoking a generic function on the object need not have any
>relation to slot access at all, much less an inherited or noninherited
>slot. This is an even stronger form of encapsulation, since the structure
>of the class is now completely hidden. Of course, an inheriting client
>may still want access to that structure.
I fully agree. It is good that CLOS allows to generate such an interface.
------------------------------------
For completeness I include the Smalltalk-80 version:
----- LAW OF DEMETER (Smalltalk-80, object version) ----
In all message expressions inside a method M
the receiver must be one of the following objects:
- an argument object of M including objects in
pseudo variables self and super or
- an instance variable object of the class to which M
is attached.
(Objects created by the method and objects in global
variables are viewed as being passed by arguments.)
--------------------------------------------------------
I am very interested in your feedback regarding the understandability
of our formulation of the Law of good style.
-- Karl