:before:after 方法允许我们将新的行为包在调用主方法的周围。 :around 方法提供了一个更戏剧的方式来办到这件事。如果 :around 方法存在的话,会调用的是 :around 方法而不是主方法。则根据它自己的判断, :around 方法自己可能会调用主方法(通过函数 call-next-method ,这也是这个函数存在的目的)。

    这称为标准方法组合机制 (standard method combination)。在标准方法组合机制里,调用一个通用函数会调用

    1. 最具体的 :around 方法,如果有的话。

    2. 否则,依序,

    辅助方法通过在 defmethod 调用中,在方法名后加上一个修饰关键字 (qualifying keyword)来定义。如果我们替 speaker 类别定义一个主要的 speak 方法如下:

    则使用 实例来调用 speak 仅印出第二个参数:

    1. > (speak (make-instance 'speaker)
    2. I'm hungry
    3. NIL

    通过定义一个 intellectual 子类,将主要的 speak 方法用 :before:after 方法包起来,

    我们可以创建一个说话前后带有惯用语的演讲者:

    1. > (speak (make-instance 'intellectual)
    2. "I am hungry")
    3. Perhaps I am hungry in some sense
    4. NIL

    无论是哪个 :before:after 方法被调用,整个通用函数所返回的值,是最具体主方法的返回值 ── 在这个情况下,为 format 函数所返回的 。

    而在有 :around 方法时,情况就不一样了。如果有一个替传入通用函数特别定义的 :around 方法,则优先调用 :around 方法,而其它的方法要看 :around 方法让不让它们被运行。一个 :around 或主方法,可以通过调用 call-next-method 来调用下一个方法。在调用下一个方法前,它使用 next-method-p 来检查是否有下个方法可调用。

    有了 :around 方法,我们可以定义另一个,更谨慎的, speaker 的子类别:

    1. (defmethod speak :around ((c courtier) string)
    2. (format t "Does the King believe that ~A?" string)
    3. (if (eql (read) 'yes)
    4. (if (next-method-p) (call-next-method))
    5. (format t "Indeed, it is a preposterous idea. ~%"))

    当传给 speak 的第一个参数是 courtier 类的实例时,朝臣 (courtier)的舌头有了 :around 方法保护,就不会被割掉了: