The ten-minute, naive, consumer/producer design pattern in CLOS. Observations:
- standard-method-combination gives :after, which is a nice way of hooking onto superclasses.
- multiple dispatch lets me spend my time doing more important things than thinking of what class a method should belong to.
- interactive and iterative development is extremely fast and keeps me in the zone, thanks to the lack of a edit-compile-fix-run cycle.
Framework
(defclass producer ()
((consumers :accessor consumers
:initform '())))
(defgeneric add-consumer (producer consumer)
(:documentation "Add a consumer (a function taking one argument)
to a broadcast's list of interested parties"))
(defgeneric remove-consumer (producer consumer)
(:documentation "Reverse of add-consumer"))
(defgeneric broadcast (producer object)
(:documentation "Broadcast an object to all registered consumers"))
(defmethod add-consumer (producer consumer)
(pushnew consumer (consumers producer)))
(defmethod remove-consumer (producer consumer)
(let ((consumers (consumers producer)))
(setf consumers (remove consumer consumers))))
(defmethod broadcast (producer object)
(dolist (consumer (consumers producer))
(funcall consumer object)))
Example subclass
(defclass direct-producer (producer)
((latest-broadcast :accessor latest-broadcast)
(latest-broadcast-p :initform nil))
(:documentation "I broadcast the latest broadcasted object when a new consumer is added"))
(defmethod add-consumer :after ((producer direct-producer) consumer)
(when (slot-value producer 'latest-broadcast-p)
(funcall consumer (latest-broadcast producer))))
(defmethod broadcast :after ((producer direct-producer) object)
(setf (slot-value producer 'latest-broadcast-p) t)
(setf (latest-broadcast producer) object))
Example code
Lisp> (let ((producer (make-instance 'producer)))
(add-consumer producer
#'(lambda (obj) (format t "I got myself a ~A object!~%" obj)))
(add-consumer producer
#'(lambda (obj) (format t "I has object: ~A~%" obj)))
(broadcast producer 'cheezburger))
I has object: CHEEZBURGER
I got myself a CHEEZBURGER object!
Lisp> (defparameter *direct-producer* (make-instance 'direct-producer))
(add-consumer *direct-producer*
#'(lambda (obj) (format t "I got myself a ~A object!~%" obj)))
(broadcast *direct-producer* 'kitty)
I got myself a KITTY object!
Lisp> (add-consumer *direct-producer*
#'(lambda (obj) (format t "I has object: ~A~%" obj)))
I has object: KITTY