DEV Community

Cover image for An example of overengineering - keep it WET
Nicolas Fränkel
Nicolas Fränkel

Posted on • Originally published at blog.frankel.ch

An example of overengineering - keep it WET

This week's post is pretty short. I've already written about overengineering, but this adds a personal touch.

I had to rewrite my Jet Train demo to use another data provider, switching from a Swiss one to a Bay Area one. One of the main components of the demo is a streaming pipeline.
The pipeline:

  1. Reads data from a web endpoint
  2. Transforms data through several steps
  3. Writes the final data into an in-memory data grid

Most of the transform steps in #2 enrich the data. Each of them requires an implementation of a BiFunction<T,U,T>.

These implementations all follow the same pattern:

  • We evaluate the second parameter of the BiFunction.
  • If it is null, we return the first parameter;
  • if not, we use the second parameter to enrich the first parameter with and return the result.

It looks like this snippet:

fun enrich(json: JsonObject, data: String?): JsonObject = if (data == null) json else JsonObject(json).add("data", data) 
Enter fullscreen mode Exit fullscreen mode

In the parlance of Object-Oriented Programming, this looks like the poster child for the Template Method pattern. In Functional Programming, this is plain function composition. We can move the null-check inside a shared function outside of the bi-function.

fun unsafeEnrich(json: JsonObject, data: String?): JsonObject = JsonObject(json).add("data", data) // 1 fun <T, U> nullSafe(f: BiFunction<T, U?, T>): BiFunction<T, U?, T> = // 2 BiFunction<T, U?, T> { t: T, u: U? -> if (u == null) t else f.apply(t, u) } val unsafeEnrich = BiFunction<JsonObject, String?, JsonObject> { json, data -> // 3 unsafeEnrich(json, data) } val safeEnrich = nullSafe(unsafeEnrich) // 4 
Enter fullscreen mode Exit fullscreen mode
  1. Move the null-check out of the function
  2. Factor the null-check into a BiFunction
  3. Create a BiFunction variable from the function
  4. Wrap the non null-safe BiFunction into the safe one

We can now test:

println(safeEnrich.apply(orig, null)) println(safeEnrich.apply(orig, "x")) 
Enter fullscreen mode Exit fullscreen mode

It works:

{"foo":"bar"} {"foo":"bar","data":"x"} 
Enter fullscreen mode Exit fullscreen mode

When I finished the code, I looked at the code and thought about the quote from Jurassic Park:

Your scientists were so preoccupied with whether or not they could, they didn't stop to think if they should.

I'm no scientist, but I felt it applied to the work I just did. I realized that I refactored in order to comply to the DRY principle. When looking at the code, it didn't look more readable and the code added to every function was minimal anyway. I threw away my refactoring work in favor of the WET principle.

There are two lessons here:

  1. Think before you code - this one I regularly forget.
  2. Don't be afraid to throw away your code.

Originally published at A Java Geek on April 25th, 2021

Top comments (0)