Wednesday, June 12, 2013

The Function of Factoring

Short story:

Last night I was fixing an ugly bug in my Scala game engine. It wasn't a particularly interesting piece of code except for the fact that it required to instantiate some user-side objects for witch it used a common Prototype pattern, as inherited from the original Java code.

Problem was quite simple: The object being used as prototype had some initialization code, that wasn't being called by the clone method:

class MyPrototypeClass(arg : ArgType) extends Cloneable {

    // Some fancy initialization code

   val bar = ...

   foo(arg)

   def foo(x : ArgType) = ...

}

...

class PrototypeConsumer(prototype : Cloneable) {

    ...


    def newGuy = prototype.clone

    ...
}

...

val c = new PrototypeConsumer(new MyPrototypeClass(arg))

val g = c.newGuy



Ok, so I know there are hundreds of ways of dealing with this... For example, I could create an initialize method  and just redefine clone to call it:


class MyPrototypeClass(arg : ArgType) {

   initialize(arg)

   def initialize(arg : ArgType) {
       // Some fancy initialization code

       val bar = ...

       foo(arg)
   }

   override def clone : this.type = super.clone.initialize

   def foo(x : ArgType) = ...
}

Problem was prototypes where supposed to be provided by the framework users and I wanted to make it as simple as possible. Plus, I'm really starting to dig this just-leave-your-initialization-code-here philosophy, and I was not about to give it up.

An alternative could have been to just clone the prototype calling it's constructor by reflection; but there again the code for that was way more complex than worthy.

So there I was, with an object with a clone mechanism that didn't behave in one hand and whole lot of object-oriented approaches in the other, when suddenly I realized I didn't need either of them. I didn't really need the user to provide me with an object that I could duplicate, but the duplication mechanism itself. What I needed was a Factory Function:


class MyPrototypeClass(arg : ArgType) {

    // Some fancy initialization code

   val bar = ...

   foo(arg)

   def foo(x : ArgType) = ...
}

...

class PrototypeConsumer(factory : Unit => Any) {

    ...

    def newGuy = factory()

}

...

val c = new PrototypeConsumer(_ => new MyPrototypeClass(arg))

val g = c.newGuy


This was so much easier for everyone. Neither the user has an initialization contract to implement nor it's restricted to Cloneable types.

Well, that's all for now. See you soon.

No comments:

Post a Comment