Hatena::Groupcadr

わだばLisperになる このページをアンテナに追加 RSSフィード

2004 | 12 |
2005 | 01 | 02 | 07 | 10 | 11 |
2006 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 11 |

2008-11-27

CLとデザインパターン - Observer

| 01:18 | CLとデザインパターン - Observer - わだばLisperになる を含むブックマーク はてなブックマーク - CLとデザインパターン - Observer - わだばLisperになる

今回はObserverパターンです。

最初にObserverパターンの説明を読んでいて内容的には、メソッドにフックが掛けられれば、それで解決できてしまうのではないかと思ったのですが、Norvig氏のDesign Patterns in Dynamic Programming でも、メソッドコンビネーション(:after)で解決していました。

:afterのフックを掛ける方が便利だとは思いましたが、クラスだけのバージョンを作ってみました。比較の為にメソッドコンビネーション版も作ってみています。

説明ですが、大域変数*numlist*に数をプッシュしてゆくpush-numがあり、push-numがプッシュするたびに全部の数を掛けたものと足したものを表示します。

メソッドコンビネーション版は、push-num2はプッシュした後:afterで、observerに通知をしています。attachとdetachの実装はしておらず関数名のリストを渡しています。

Norvig氏の説明によれば、メソッドコンビネーションであれば、subjectクラスの用意もいらないだろうとのこと。

(defclass observer () ())
(defclass sum (observer) ())
(defclass mult (observer) ())

(defgeneric update (obs)
  (:method ((obs observer))))

(defmethod update ((obs sum))
  (format T "sum: = ~A~%" (apply #'+ *numlist*)))

(defmethod update ((obs mult))
  (format T "mult: = ~A~%" (apply #'* *numlist*)))

(defclass subject () 
  ((observers :initform ())))
(defun make-subject ()
  (make-instance 'subject))

(defgeneric attach (sub obs)
  (:method ((sub subject) (obs observer))
    (pushnew obs (slot-value sub 'observers))))

(defgeneric detach (sub obs)
  (:method ((sub subject) (obs observer))
    (with-slots ((observers observers)) sub
      (setf observers
            (delete-if (lambda (x) (eq x obs))
                       observers)))))

(defgeneric notify (sub)
  (:method ((sub subject))
    (map nil #'update (slot-value sub 'observers))))

(defvar *numlist* () )

(defun push-num (sub num)
  (push num *numlist*)
  (notify sub)))

;; 動作
(defparameter *ob1* (make-instance 'sum))
(defparameter *ob2* (make-instance 'mult))
(defparameter *sub* (make-subject)))

(attach *sub* *ob1*)
(attach *sub* *ob2*)

(push-num *sub* 2)
;>>>
sum: = 2
mult: = 2
...
sum: = 10
mult: = 32
;; メソッドコンビネーション(:after)版
(defvar *numlist2* () )

(defmethod push-num2 (sub (n number))
  (push n *numlist2*))

(defmethod push-num2 :after (sub (n number))
  (map nil #'funcall sub))

(defun sum ()
  (format T "sum: = ~A~%" (apply #'+ *numlist2*)))

(defun mult ()
  (format T "mult: = ~A~%" (apply #'* *numlist2*)))

;; 動作
(push-num2 '(sum mult) 2)
;>>>
sum: = 2
mult: = 2
...
sum: = 10
mult: = 32