如何使用方法组合减少代码重复,但保持可能的早期返回

时间:2013-08-22 15:23:07

标签: common-lisp clos

我有一组表示必须处理的消息的类。但是处理者的开放点数量有限。因此,处理消息对象的处理程序的任何“调度”必须首先检查是否存在空闲点。

如果有 - >调度。

如果没有 - >不发送和返回相应的消息

由于这部​​分代码在任何调度方法中都是相同的,我认为最好使用方法组合工具来强制执行,但我无法弄清楚如何。

在我目前的代码库中,我尝试使用:before方法,但显然你不能在这样的上下文中使用return:

(defclass message () ((msg :initarg :msg :reader msg)))

(defclass message-ext (message) 
    ((univ-time :initarg :univ-time :reader univ-time)))

(defparameter *open-handler* nil)

(defgeneric handle (message)
  (:documentation "handle the given message appropriately"))

(defmethod handle :before ((message message))
  (when (> (length *open-handler*) 1)
    (return :full)))

(defmethod handle ((message message))
  (push (FORMAT nil "dispatched handler") *open-handler*))

(defmethod handle ((message-ext message-ext))
  (push (FORMAT nil "dispatched ext handler") *open-handler*))

(handle (make-instance 'message :msg "allemeineentchen"))

(handle (make-instance 'message-ext 
                       :msg "rowrowrowyourboat" 
                       :univ-time (get-universal-time)))

(handle (make-instance 'message-ext 
                       :msg "gentlydownthestreet" 
                       :univ-time (get-universal-time)))

Execution of a form compiled with errors.
Form:
  (RETURN-FROM NIL FULL)
Compile-time error:
  return for unknown block: NIL
   [Condition of type SB-INT:COMPILED-PROGRAM-ERROR]

Restarts:
 0: [RETRY] Retry SLIME interactive evaluation request.
 1: [*ABORT] Return to SLIME's top level.
 2: [TERMINATE-THREAD] Terminate this thread (#<THREAD "worker" RUNNING {100594F743}>)

Backtrace:
  0: ((SB-PCL::FAST-METHOD HANDLE :BEFORE (MESSAGE)) #<unavailable argument> #<unavailable argument> #<unavailable argument>)
  1: ((SB-PCL::EMF HANDLE) #<unavailable argument> #<unavailable argument> #<MESSAGE-EXT {1005961733}>)
  2: (SB-INT:SIMPLE-EVAL-IN-LEXENV (HANDLE (MAKE-INSTANCE 'MESSAGE-EXT :MSG "gentlydownthestreet" :UNIV-TIME (GET-UNIVERSAL-TIME))) #<NULL-LEXENV>)
  3: (EVAL (HANDLE (MAKE-INSTANCE 'MESSAGE-EXT :MSG "gentlydownthestreet" :UNIV-TIME (GET-UNIVERSAL-TIME))))
  4: ((LAMBDA () :IN SWANK:INTERACTIVE-EVAL))

这种方法是否理智,如果是,我怎么能以工作的方式做到这一点? (我已经尝试return-from使用相同的结果)

2 个答案:

答案 0 :(得分:4)

我认为您应该使用:around方法限定符:

(defmethod handle :around ((message message))
  (if (cddr *open-handler*)
      :full
      (call-next-method)))

然而,更“懒散”的做法是使用CL Condition System,例如:

(define-condition too-many-messages (...) (...) ...)
(defun add-message (message)
  (when (cddr *open-handler*)
    (signal 'too-many-messages))
  (push message *open-handler*))
(defmethod handle ((message message))
  (add-message (FORMAT nil "dispatched handler")))

除了检查handle函数的返回值之外,您还必须处理条件(使用例如handler-bind)。

PS。在列表上调用length以检查它是否足够长并不是一个好主意 - 尽管在您的情况下,当保证列表很短时,这可能更像是样式问题。

PPS。使用单词handle作为函数名称不是一个好主意,因为CL具有包含它的函数(例如,handler-case)。除了让阅读代码的人感到困惑之外,这会使代码中的搜索变得复杂。

答案 1 :(得分:1)

你无法调用RETURN从这样的函数返回。

您需要将RETURN-FROM与函数名称一起使用。但是这里它将从方法返回 - 而不是泛型函数。

@sds有一个答案。另一个是发信号通知用户定义的条件并在其他地方处理它。旧代码使用catchthrow

更复杂的任务是用户定义的方法组合。