I want to build a set of builders for generating test data. On one side I want my builders API to be natural to use for my teammates. On the other side I'm great fun of creating DSLs, because it eliminates writing the same boilerplate code and let you read the code as English sentences.
However, I found those requirements quite incompatible. I've shown my builders to my teammate and what was intuitive to me, was not for him. He would need to learn my DSL.
How would you improve my design decisions in Builder pattern?
Decision #1: Long domain names vs. new short names
I started with traditional withX()
methods for setting field X. For X I used names that are well known in our domain and commonly used in the rest of implementation.
Flight.Builder.create()
.withOrigin(Airport.LAX)
.withDestination(Airport.JFK)
.build();
Then I thought this takes too much space, so I used method names that are shorter but uncommon in the rest of the implementation.
Flight.Builder.create()
.from(Airport.JFK)
.to(Airport.LAX)
.build();
Another programmer prefered traditional names from the domain: origin, destination.
Decision #2: Clear method names vs. duplicate methods with type-overloading
Another weak point I found was redundancy of some information. For instance, in the following code:
Flight.Builder.create()
.withPassenger(Passenger.ADULT) // that's a template
.withPassenger(Passenger.Builder.create().withName("KENNEDDY"))
.withStatus(Status.CANCELLED)
.build();
the redundancy is in passenger-passenger, and in status-status. So I removed redudant words from method names and used type-overloading.
Flight.Builder.create()
.with(Passenger.ADULT)
.with(Passenger.Builder.create().withName("KENNEDDY"))
.with(Status.CANCELLED)
.build();
The programmer who knows data model underlying this builder and was used to more traditional builder pattern, was confused with my approach.
Decision #3: Changing words order and delegating builder creation
Then I found builder creation kind of boilerplate code that makes it harder to read:
Flight.Builder.create()
.withPassenger(Passenger.Builder.create().withName("KENNEDDY"))
.build();
I shortened those constructs. I also found this incompatible with English words order in sentence, where you don't say "what.build()", but "build(what)". Here's the result.
build(flight()
.withPassenger(passenger().withName("KENNEDDY")));
How would you improve my design decisions?