11.8 方法组合机制 (Method Combination)

在标准方法组合中,只有最具体的主方法会被调用(虽然它可以通过 call-next-method 来调用其它方法)。但我们可能会想要把所有可用的主方法的结果汇总起来。

用其它组合手段来定义方法也是有可能的 ── 举例来说,一个返回所有可用主方法的和的通用函数。操作符 (Operator)方法组合可以这么理解,想像它是 Lisp 表达式的求值后的结果,其中 Lisp 表达式的第一个元素是某个操作符,而参数是按照具体性调用可用主方法的结果。如果我们定义 price 使用 + 来组合数值的通用函数,并且没有可用的 :around 方法,它会如它所定义的方式动作:

  1. (defun price (&rest args)
  2. (+ (apply most specific primary method args)
  3. .
  4. .
  5. .
  6. (apply least specific primary method args)))

如果有可用的 :around 方法的话,它们根据优先级决定,就像是标准方法组合那样。在操作符方法组合里,一个 around 方法仍可以通过 call-next-method 调用下个方法。然而主方法就不可以使用 call-next-method 了。

我们可以指定一个通用函数的方法组合所要使用的类型,借由在 defgeneric 调用里加入一个 method-combination 子句:

  1. (defgeneric price (x)
  2. (:method-combination +))

现在 price 方法会使用 + 方法组合;任何替 price 定义的 defmethod 必须有 + 来作为第二个参数。如果我们使用 price 来定义某些类型,

  1. (defclass jacket () ())
  2. (defclass trousers () ())
  3. (defclass suit (jacket trousers) ())
  4. (defmethod price + ((jk jacket)) 350)
  5. (defmethod price + ((tr trousers)) 200)

则可获得一件正装的价钱,也就是所有可用方法的总和:

  1. > (price (make-instance 'suit))
  2. 550

下列符号可以用来作为 defmethod 的第二个参数或是作为 defgeneric 调用中,method-combination 的选项:

  1. + and append list max min nconc or progn

你也可以使用 standard ,yields 标准方法组合。

一旦你指定了通用函数要用何种方法组合,所有替该函数定义的方法必须用同样的机制。而现在如果我们试着使用另个操作符( :beforeafter )作为 defmethodprice 的第二个参数,则会抛出一个错误。如果我们想要改变 price 的方法组合机制,我们需要通过调用 fmakunbound 来移除整个通用函数。