2013-08-22 26 views
5

Ho ottenuto un set di classi che rappresentano un messaggio che deve essere gestito. Ma c'è solo una quantità limitata di punti aperti per i gestori. Pertanto, ogni "invio" di un gestore che gestisce un oggetto messaggio deve verificare prima se c'è un posto libero.Come ridurre la duplicazione del codice utilizzando la combinazione di metodi ma mantenendo possibile il ritorno anticipato

Se c'è -> invio.

Se non c'è -> non inviare e restituire un messaggio

corrispondente Dato che questa parte del codice sarà lo stesso in qualsiasi metodo di spedizione ho pensato che sarebbe stato meglio utilizzare l'impianto metodo di combinazione di far rispettare che ma non riesco a capire come.

Nella mia base di codice corrente ho cercato di usare una: prima di metodo, ma a quanto pare non è possibile utilizzare il ritorno in tale contesto:

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

È questo approccio ancora sano di mente, e se sì, come posso farlo in una moda lavorativa? (Ho già provo return-from con lo stesso risultato)

risposta

4

penso che si dovrebbe utilizzare il qualificatore :around metodo invece:

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

Tuttavia, un approccio più "lispy" è quello di utilizzare il CL Condition System, ad esempio, qualcosa di simile:

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

Si dovrà gestire la condizione (utilizzando, ad esempio, handler-bind) oltre a controllare i valori di ritorno della vostra funzione handle.

PS. Chiamare length in un elenco per verificare che sia abbastanza lungo non è una buona idea, anche se nel tuo caso, quando la lista è garantita per essere breve, questo potrebbe essere più di un problema di stile.

PPS. Non è una buona idea usare la parola handle come nome della funzione perché CL ha funzioni che la contengono (ad esempio, handler-case). Ciò complicherà la ricerca nel tuo codice oltre a confondere le persone che leggono il tuo codice.

1

Non è possibile chiamare RETURN per tornare da una funzione del genere.

È necessario utilizzare RETURN-FROM con il nome della funzione. Ma qui ritornerebbe dal metodo, non dalla funzione generica.

@ sds ha una risposta. Un altro potrebbe segnalare una condizione definita dall'utente e gestirla da un'altra parte. Codice precedente utilizzato catch e throw.

Un'impresa più complessa sarebbe una combinazione di metodi definita dall'utente.