A Silly Consumer/Producer

In topic Article :: Published Monday, September 29, 2008

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

If this is the first time you comment on my site, your post might get held up in the moderation queue. Don't panic, I'll approve it as soon as possible!

Code (i.e. literal) blocks should be wrapped inside <PRE>.

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Close
Powered by ShareThis