Thursday, August 27, 2009

Builder Pattern in Clojure

We've been working steadily on our toy traffic simulator and doing TDD along the way. Of course a major concept in a traffic simulator is a car, so we of course have a car struct + cars have some behavior patterns so we also have a behavior struct. They look like:

(defstruct behavior :accel-dist :decel-dist)
(defstruct car :name :position :length :speed :behavior)

So to construct a valid car you need to do something like:

(struct car :a 1 1 1 (struct behavior 10 5)))

Yikes, we constantly wanted cars for our tests and constructing a car like that was often more code than the real stuff we wanted to test. So, we made a bunch of convenience methods (object mother type stuff), like:

(defn slow-car [name position]
 (struct car name position 1 1 (struct behavior 10 5)))

(defn car-with-speed-position [speed position]
 (struct car :a position 1 speed (struct behavior 10 5)))

Suddenly we started creating lots of car-with-* methods. We got annoyed and accidentally started implementing the builder pattern.

(defn default-car []
 (struct car :a 1 1 1 (struct behavior 10 5)))

(defn car-with 
 ([key value]
  (assoc (default-car) key value))
 ([key1 value1 key2 value2]
  (assoc (car-with key1 value1) key2 value2)))

Ignore the total ugliness of the car-with function for a moment, this let us do things like:

(def a-car (car-with :speed 1 :position 4))

as opposed to:

(def a-car (car-with-speed-position 1 4))

Our new version, was not only easier to read, and didn't make us create new methods for each different thing we wanted to do, but hey, that looks really similar to the builder pattern. Now I'm sure there are better ways to implement car-with, but it works for now.

No comments:

Web Statistics