Chain Of Responsibility

日記などを振り返ると、CLOSの勉強をしていたのが今からちょうど1年前だったようです。忘れまくり。。。
復習しなくてはなりません。なぜなら僕はジェダイだから・・・!(ジェダイの復習
どうせなのでGoFのパターンとメタプログラミングをごちゃ混ぜにして脳の筋トレをしよう、ということで、気が向いた時(主に息抜きしたい時か現実逃避したい時)に気が向いたパターンをCLOSで書いてみようかと思います。
今回はその第一回目としてChain Of Responsibility.
ちなみに@id:uskz さんのわんくまでの発表に若干影響されました。(でもC++分からないので・・・っていう

;; ネーミングはGoFから拝借しています
CL-USER> (defclass Handler ()
           ((successor :initarg :Successor))) ;; :initargはmake-instance時に値を初期化できるようになる
CL-USER> (defclass ConcreteHandler1 (Handler) ()) ;; 継承するだけでスロットは空
CL-USER> (defclass ConcreteHandler2 (Handler) ())
CL-USER> (defclass ConcreteHandler3 (Handler) ())

;; 各メソッドの構造が同じなのでメタる。
;; (自分が対処できないデータは次に押し付ける、というメソッド)
CL-USER> (defmacro define-HandleRequest (type var pred then)
           `(defmethod HandleRequest ((h ,type) ,var)
             (if ,pred
                 ,then
                 (HandleRequest (slot-value h 'successor) ,var))))

;; どのように展開されるかと言うと・・・
CL-USER> (macroexpand-1 '(define-HandleRequest ConcreteHandler1 x
                          (eq x 'one) "one !"))
; -> (DEFMETHOD HANDLEREQUEST ((H CONCRETEHANDLER1) X)
;      (IF (EQ X 'ONE) "one !" (HANDLEREQUEST (SLOT-VALUE H 'SUCCESSOR) X)))
; => T

;; マクロを使ってメソッドを定義する
CL-USER> (define-HandleRequest ConcreteHandler1 x
           (eq x 'one) "one !")
CL-USER> (define-HandleRequest ConcreteHandler2 x
           (eq x 'two) "two !!")
CL-USER> (define-HandleRequest ConcreteHandler3 x
           (eq x 'three) "three !!!")


;; 親(Handler)クラスが持つsuccessorスロットにどんどん入れる
CL-USER> (setq chain
               (make-instance 'ConcreteHandler1
                              :Successor (make-instance 'ConcreteHandler2
                                                        :Successor (make-instance 'ConcreteHandler3))))

;; 実験
CL-USER> (HandleRequest chain 'one)
"one !"
CL-USER> (HandleRequest chain 'two)
"two !!"
CL-USER> (HandleRequest chain 'three)
"three !!!"
CL-USER> (HandleRequest chain 'four) ;; successorが空っぽのため
; Evaluation aborted