[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Law of Good Style

Laws of good style are like laws of good taste - hard to define and
changeable. Also, they are debatable and not everyone will eventually
agree with them.

I've put my Debunker team on this problem. Here is the latest version of
the law (I wonder: Will the people whose comments on the law alter it be
made co-authors on the OOPSLA paper?):

----- 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.)

We need some classes, so let's randomly pick the ones Karl suggested:

;;; The slot named B in instances of A contain objects of type B
(defclass A () ((B :type B)))
;;; The slot named C in instances of B contain objects of type C
(defclass B () ((C :type C)))
;;; The slot named D in instances of C contain objects of type D
(defclass C () ((D :type D))) 

Here is a method that obeys the law:

(defmethod v ((p1 A) (p2 Z))
  (v1 (slot-value p1 'b)))

Let's look at v1:

(defmethod v1 ((p B))
  (v2 (slot-value p 'c)))

That also obeys the law. Let's look at v2:

(defmethod v2 ((p C))
  (slot-value p 'd))

This one also obeys the law. Let's combine these using generic-labels

(defmethod v ((p1 A) (p2 Z))
  (generic-labels ((v1 (:method ((p B)) (v2 (slot-value p 'c))))
		   (v2 (:method ((p C)) (slot-value p 'd))))
    (v1 (slot-value p 'b))))

Since this is really the same program as the previous set of methods, it
must also obey the law, though the law doesn't seem to clearly treat local

Let's assume that we are freezing the classes A, B, and C. Now we can
decorate this function with some declarations:

(defmethod v ((p1 A) (p2 Z))
  (generic-labels ((v1 (:method ((p B)) (v2 (the C (slot-value p 'c)))))
		   (v2 (:method ((p C)) (slot-value p 'd))))
    (v1 (the B (slot-value p 'b)))))

Now let's inline these procedures:

(defmethod v ((p1 A) (p2 Z))
  (slot-value (slot-value (slot-value p1 'b) 'c) 'd))

And while we're at it, let's rename the function:

(defmethod violation ((p1 A) (p2 Z))
  (slot-value (slot-value (slot-value p1 'b) 'c) 'd))