2013-08-22 3 views
5

Ich habe eine Reihe von Klassen erhalten, die eine Nachricht darstellen, die behandelt werden muss. Es gibt jedoch nur eine begrenzte Anzahl von offenen Stellen für Handler. Daher muss jeder "Dispatch" eines Handlers, der ein Nachrichtenobjekt behandelt, zuerst prüfen, ob ein freier Platz vorhanden ist.Wie man die Codeverdoppelung unter Verwendung der Methodenkombination reduziert, aber mögliche frühe Rückkehr behält

Wenn es -> Versand ist.

Wenn gibt es nicht -> Versand nicht an und bringt Nachricht

Da dieser Teil des Codes entspricht, wird das gleiche in jedem Versandweg sein ich es dachte, am besten wäre, die Methode Kombination Anlage zu verwenden, um durchzusetzen, dass , aber ich kann nicht herausfinden, wie.

In meinem aktuellen Codebasis habe ich versucht, eine verwenden: vor Methode, aber anscheinend kann man nicht Rückkehr in einem solchen Kontext verwenden:

(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)) 

Ist dieser Ansatz sogar gesund, und wenn ja, wie kann ich es tun in eine funktionierende Mode? (Ich habe schon versucht return-from mit dem gleichen Ergebnis)

Antwort

4

Ich denke, Sie sollten den :around Methode Qualifier verwenden statt:

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

jedoch ein „lispy“ Ansatz ist die CL Condition System, zum Beispiel zu verwenden, so etwas wie diese:

(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"))) 

Sie die Bedingung (zB unter Verwendung handler-bind) zusätzlich zur Überprüfung des Rückgabewertes Ihrer handle Funktion haben zu behandeln.

PS. Das Anrufen von length auf einer Liste, um zu überprüfen, dass es lang genug ist, ist keine sehr gute Idee - obwohl in Ihrem Fall, wenn die Liste garantiert kurz ist, könnte dies eher ein Stilproblem sein.

PPS. Es ist keine sehr gute Idee, das Wort handle als einen Namen Ihrer Funktion zu verwenden, da CL Funktionen aufweist, die es enthalten (z. B. handler-case). Dies erschwert die Suche in Ihrem Code zusätzlich zu verwirrenden Personen, die Ihren Code lesen.

1

Sie können RETURN nicht aufrufen, um von einer solchen Funktion zurückzukehren.

Sie müssten RETURN-FROM mit dem Funktionsnamen verwenden. Aber hier würde es von der Methode zurückkehren - nicht die generische Funktion.

@ sds hat eine Antwort. Ein anderes wäre, eine benutzerdefinierte Bedingung zu signalisieren und anderswo zu behandeln. Älterer Code verwendet catch und throw.

Ein komplexeres Unterfangen wäre eine benutzerdefinierte Methodenkombination.

Verwandte Themen