LISP - `:around` 方法组合
Lisp - :around 方法组合
Section titled “Lisp - :around 方法组合”Common Lisp 对象系统 (CLOS) 提供了一个强大的特性,称为方法组合(method combination)。标准方法组合使用主方法(primary methods)以及 :before、:after 和 :around 辅助方法(auxiliary methods)。其中,:around 是最强大的,因为它允许你编写在主方法之前和之后运行的代码,从而有效地创建一个包装器。
可以把 :around 方法想象成 Web 框架中的中间件或 Python 中的装饰器。它可以检查参数、设置特殊上下文、运行核心逻辑,然后处理结果或执行清理工作。
定义 :around 方法
Section titled “定义 :around 方法”:around 方法通过在 defmethod 中添加 :around 限定符来定义。其强大之处在于 call-next-method 函数。
;; 1. 定义一个泛型函数。(defgeneric process-data (data))
;; 2. 为此泛型函数定义一个 :around 方法。(defmethod process-data :around ((data t)) (format t "[Wrapper] Before processing data: ~a~%" data)
;; 3. 使用 `call-next-method` 调用下一个最特化的方法。 (let ((result (call-next-method)))
(format t "[Wrapper] After processing. Result was: ~a~%" result)
;; 4. :around 方法可以返回原始结果或修改后的结果。 result))call-next-method 函数将控制权转移给下一个适用的方法(可以是另一个 :around 方法或主方法)。如果没有 call-next-method,主方法将不会被执行。
当 process-data 被调用时,流程如下:
:around方法首先被调用。call-next-method之前的代码运行。call-next-method调用适用的主方法(或其他辅助方法)。- 主方法运行并返回一个值。
- 控制权返回到
:around方法,返回值绑定到result。 call-next-method之后的代码运行。:around方法返回其最终值。
让我们添加针对不同数据类型特化的主方法。
;;; --- 泛型函数和 :around 方法(同上)---(defgeneric process-data (data))
(defmethod process-data :around ((data t)) (format t "[Wrapper] Before processing data: ~a~%" data) (let ((result (call-next-method))) (format t "[Wrapper] After processing. Result was: ~a~%" result) result))
;;; --- 主方法 ---
;; 定义一个针对整数特化的主方法。(defmethod process-data ((data integer)) (format t " (Primary) Processing an integer...~%") (* data 2))
;; 定义一个针对字符串特化的主方法。(defmethod process-data ((data string)) (format t " (Primary) Processing a string...~%") (string-upcase data))
;;; --- 调用 ---
(format t "--- Calling with an integer ---~%")(process-data 10)
(format t "~%--- Calling with a string ---~%")(process-data "hello world")--- Calling with an integer ---[Wrapper] Before processing data: 10 (Primary) Processing an integer...[Wrapper] After processing. Result was: 20
--- Calling with a string ---[Wrapper] Before processing data: hello world (Primary) Processing a string...[Wrapper] After processing. Result was: "HELLO WORLD"常见用例和陷阱
Section titled “常见用例和陷阱”何时使用 :around
Section titled “何时使用 :around”- 资源管理:确保文件或网络连接等资源被关闭(例如,在
:around方法中使用unwind-protect)。 - 缓存/记忆化:在调用
call-next-method之前检查结果是否已缓存。 - 日志和追踪:在进入时打印参数,在退出时打印结果。
- 参数验证:在将参数传递给主方法之前进行验证。
- 条件执行:根据某些条件决定是否调用主方法。
- 忘记
call-next-method:这是最常见的错误。如果你省略此调用,主方法将永远不会运行,你的泛型函数很可能不会达到你预期的效果。 - 错误地修改参数:如果你想为下一个方法更改参数,你必须将它们传递给
call-next-method:(call-next-method new-arg1 new-arg2)。