Functional Objects
It is not an oxymoron.
I had occasion to work with an interesting data migration recently, and faced a number of design decisions. On the one hand I could add to an existing, procedurally-oriented Rails code base. On the other hand, I could design something a bit more object oriented which might take a little bit longer. However, the OO decision would be accompanied by test coverage, and the resulting object would be purely functional, carrying only ephemeral, local state if any.
The pattern is called the “Function Object” in Evan’s Domain Driven Design lexicon. The characteristics of the function object are:
- Carries only ephemeral, local state at most.
- Has no side effects.
- Behaves like a mathematical function, the only operations are computation or transformation, and returns the result of that operation.
- Thread safe.
An obvious question here is how much more time will this take over writing the transformation inline? The answer it depends, and should range from “very little” to “some.” Very little for untested code, which is the equivalent of copying what would be inline into it’s own function (or class wrapper in OO languages). Depending on one’s proficiency with testing, line and branch coverage will take a bit more time, if only because it requires more typing.
What may be a suprise to some is when edge cases show up when testing is written. Handling such edge cases does take time. But this is a gift. It’s ultimately cheaper to proactively mitigate an edge case than it is to retroactively remediate that same case when it comes in as a bug report.
As it turned out, the functionality I needed to add for the data migration mentioned above was best implemented directly in the appropriate Rails model. However, I have applied the pattern in another part of the same code base, a worthy exercise.