Do you want your ad here?

Contact us to get your ad seen by thousands of users every day!

[email protected]

An Example of Overengineering: Keep it WET

  • April 28, 2021
  • 1724 Unique Views
  • 2 min read

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.

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)

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
  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"))

It works:

{"foo":"bar"}
{"foo":"bar","data":"x"}

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 14th, 2021

Do you want your ad here?

Contact us to get your ad seen by thousands of users every day!

[email protected]

Comments (2)

Highlight your code snippets using [code lang="language name"] shortcode. Just insert your code between opening and closing tag: [code lang="java"] code [/code]. Or specify another language.

Tobiloba avatar

Tobiloba

3 years ago

I see that you save the point of interest as text in the DB but the response gotten from ChatGPT is JSON. Does this mean you convert the response into string using libraries like gson before saving it in the database?

15

Highlight your code snippets using [code lang="language name"] shortcode. Just insert your code between opening and closing tag: [code lang="java"] code [/code]. Or specify another language.

Denis Magda avatar

Denis Magda

3 years ago

Hey, The response is a String object in the JSON format [1]. The repository takes this JSON string as is and stores to the database [2]. Presently, Spring Data auto-generates the CREATE TABLE statement on the startup and sets the "point of interest" column's type to "text" (or "varchar", don't remember). However, it's always possible to ask Spring Data to use the "json" or "jsonb" type for the column if you wish to query the JSON at the database level. Finally, Vaadin displays a list of PointsOfInterests. Those are generated using the org.json library [3]. Let me know if you have other questions. Hope this helps. [1] https://github.com/YugabyteDB-Samples/budget-journey-gpt/blob/main/src/main/java/com/yugabyte/com/TripsAdvisorService.java#L103 [2] https://github.com/YugabyteDB-Samples/budget-journey-gpt/blob/main/src/main/java/com/yugabyte/com/TripsAdvisorService.java#L74 [3] https://github.com/YugabyteDB-Samples/budget-journey-gpt/blob/main/src/main/java/com/yugabyte/com/TripsAdvisorService.java#L114

23

Highlight your code snippets using [code lang="language name"] shortcode. Just insert your code between opening and closing tag: [code lang="java"] code [/code]. Or specify another language.

Subscribe to foojay updates:

https://foojay.io/feed/
Copied to the clipboard