17.5 定义方法 (Defining Methods)

到目前为止,我们借由叙述如下的东西来定义一个方法:

  1. (defprop area t)
  2. (setf circle-class (obj))
  3. (setf (area circle-class)
  4. #'(lambda (c) (* pi (expt (radius c) 2))))
  1. (defmacro defmeth (name obj parms &rest body)
  2. (let ((gobj (gensym)))
  3. `(let ((,gobj ,obj))
  4. (setf (gethash ',name ,gobj)
  5. (labels ((next () (get-next ,gobj ',name)))
  6. #'(lambda ,parms ,@body))))))
  7. (defun get-next (obj name)
  8. (some #'(lambda (x) (gethash name x))
  9. (cdr (gethash :preclist obj))))

图 17.6 定义方法。

在一个方法里,我们可以通过给对象的 :preclistcdr 获得如内置 call-next-method 方法的效果。所以举例来说,若我们想要定义一个特殊的圆形,这个圆形在返回面积的过程中印出某个东西,我们可以说:

  1. (setf grumpt-circle (obj circle-class))
  2. (setf (area grumpt-circle)
  3. #'(lambda (c)
  4. (format t "How dare you stereotype me!~%")
  5. (funcall (some #'(lambda (x) (gethash 'area x))
  6. (cdr (gethash :preclist c)))
  7. c)))

这里 funcall 等同于一个 call-next-method 调用,但他..

图 17.6 的 defmeth 宏提供了一个便捷方式来定义方法,并使得调用下个方法变得简单。一个 defmeth 的调用会展开成一个 setf 表达式,但 setf 在一個 labels 表达式里定义了 next 作为取出下个方法的函数。这个函数与 next-method-p 类似(第 188 页「譯註: 11.7 節」),但返回的是我们可以调用的东西,同時作為 call-next-methodλ 前述两个方法可以被定义成:

  1. (defmeth area circle-class (c)
  2. (* pi (expt (radius c) 2)))
  3. (defmeth area grumpy-circle (c)
  4. (format t "How dare you stereotype me!~%")
  5. (funcall (next) c))

顺道一提,注意 defmeth 的定义也利用到了符号捕捉。方法的主体被插入至函数 next 是局部定义的一个上下文里。