:before
与 :after
方法允许我们将新的行为包在调用主方法的周围。 :around
方法提供了一个更戏剧的方式来办到这件事。如果 :around
方法存在的话,会调用的是 :around
方法而不是主方法。则根据它自己的判断, :around
方法自己可能会调用主方法(通过函数 call-next-method
,这也是这个函数存在的目的)。
这称为标准方法组合机制 (standard method combination)。在标准方法组合机制里,调用一个通用函数会调用
最具体的
:around
方法,如果有的话。否则,依序,
辅助方法通过在 defmethod
调用中,在方法名后加上一个修饰关键字 (qualifying keyword)来定义。如果我们替 speaker
类别定义一个主要的 speak
方法如下:
则使用 实例来调用 speak
仅印出第二个参数:
> (speak (make-instance 'speaker)
I'm hungry
NIL
通过定义一个 intellectual
子类,将主要的 speak
方法用 :before
与 :after
方法包起来,
我们可以创建一个说话前后带有惯用语的演讲者:
> (speak (make-instance 'intellectual)
"I am hungry")
Perhaps I am hungry in some sense
NIL
无论是哪个 :before
或 :after
方法被调用,整个通用函数所返回的值,是最具体主方法的返回值 ── 在这个情况下,为 format
函数所返回的 。
而在有 :around
方法时,情况就不一样了。如果有一个替传入通用函数特别定义的 :around
方法,则优先调用 :around
方法,而其它的方法要看 :around
方法让不让它们被运行。一个 :around
或主方法,可以通过调用 call-next-method
来调用下一个方法。在调用下一个方法前,它使用 next-method-p
来检查是否有下个方法可调用。
有了 :around
方法,我们可以定义另一个,更谨慎的, speaker
的子类别:
(defmethod speak :around ((c courtier) string)
(format t "Does the King believe that ~A?" string)
(if (eql (read) 'yes)
(if (next-method-p) (call-next-method))
(format t "Indeed, it is a preposterous idea. ~%"))
当传给 speak
的第一个参数是 courtier
类的实例时,朝臣 (courtier)的舌头有了 :around
方法保护,就不会被割掉了: