Taking Advantage of Scala 2.8: Replacing the Builder

In Scala 2.8, using the builder pattern is no longer necessary (or the most optimal solution) in many cases, as Scala 2.8 adds support for named and default parameters.

I’ll give an example of this based on ScalaBox2D. In the Box2D object model, physics Bodies are defined mostly by a set of Fixtures, which in turn are defined by a Shape, density, friction, restitution and some other parameters.

In the Scala 2.7 version of ScalaBox2D, there were mutable fixture definitions, which were used by the engine to create the actual fixtures. The user code only worked with definitions, through an internal DSL that looked something like this (note: all code examples are simplified for clarity):

body {
  box(halfWidth, halfHeight) density 1 friction 0.3f restitution 0
  computeMassFromShapes
}

For the DSL implementation, I used simple builders which left values to defaults if not specified:

def box(halfW: Float, halfH: Float) = new FixtureBuilder(FixtureDef(PolygonDef.box(halfW, halfH)))

class FixtureBuilder(defn: FixtureDef) {
  def userData(userData: AnyRef) = { defn.userData = userData; this }
  def material(material: Material) = { defn.apply(material); this }
  def friction(friction: Float) = { defn.friction = friction; this }
  def restitution(restitution: Float) = { defn.restitution = restitution; this }
  def density(density: Float) = { defn.density = density; this }
  def filter(filter: FilterData) = { defn.filter = filter; this }
  def sensor(isSensor: Boolean) = { defn.isSensor = isSensor; this }
  def define = defn
}

The FixtureDefs are mutable mostly to simplify the builder. In Scala 2.8, I can use named and default parameters, and drop some more lines of code by not having builders at all. As a bonus, I can easily make the definitions immutable.

def fixtures(fd: FixtureDef*) {...}
val fixture = FixtureDef // a shorthand to the companion object of FixtureDef
val box = BoxDef // a shorthand to a BoxDef object that creates PolygonDefs

case class FixtureDef(
  shapeDef: ShapeDef,
  /** The friction coefficient, usually in the range [0,1]. */
  friction: Float = 0.2f,
  /** The restitution (elasticity) usually in the range [0,1]. */
  restitution: Float = 0f,
  /** The density, usually in kg/m^2. */
  density: Float = 0f,
  /** A sensor collects contact information but never generates a collision response. */
  isSensor: Boolean = false,
  /** Contact filtering data. */
  filter: FilterData = FilterData.Default,
  /** Use this to store application specific fixture data. */
  userData: AnyRef = null
)

As you can see, the FixtureDef is now a case class with immutable parameters that have default values. Previously it looked very similar, but had only one parameter (ShapeDef) and all fields were mutable. The usage of the “DSL” now becomes a little bit more verbose (maybe I shouldn’t even call it a DSL any more) but I think it also becomes easier to understand for someone who knows the language but not the library, due to less moving parts and using built-in features instead of more code:

body {
  fixtures(
    fixture(box(halfWidth, halfHeight), density = 1, friction = 0.3f, restitution = 0)
  )
  computeMassFromShapes
}

Deleting code while maintaining functionality always makes me glad and this is one of those cases. I guess there may be some more complex cases where builders may still work better, but for simple things like the above example, I really like the named and default arguments feature.

Note: moving ScalaBox2D to Scala 2.8 is still a work in progress for me and there may be some further changes to this “DSL” as well.

Leave a comment