2
\$\begingroup\$

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?

\$\endgroup\$
  • \$\begingroup\$ Is my question too loooong or title unclear? Any feedback is welcome. \$\endgroup\$ – dzieciou Sep 29 '12 at 8:48
  • \$\begingroup\$ The question and the title seem fine although it might fit better to Programmers.SE. \$\endgroup\$ – palacsint Sep 29 '12 at 10:38
1
\$\begingroup\$

A few, rather subjective thoughts:

Decision #1:

Here you have a flight builder. Which group of sentences looks more natural from the following?

  • a flight builder with origin
  • a flight builder with destination

or

  • a flight builder from
  • a flight builder to

The first two looks better for me.

Furthermore, if origin and destination are in your domain you should use them. Using synonyms could be confusing.

Decision #2:

I may be worth to note that if you have more than one field with the same type (strings and ints are common) it would not work. Having builders in the same application and one half of them is using with() while the other half is using withX() is also confusing.

Decision #3:

The passenger().withName("KENNEDDY") statements make me think about where the passenger instance comes from. A database or maybe a cache? I think a create word somewhere would make it obvious that it's a new instance and it would be helpful for readers although renaming passenger() to createPassenger() does not seem natural in this code too.

\$\endgroup\$
  • 1
    \$\begingroup\$ Regarding passenger creation. In our business domain passenger instances are not shared across flight. Instead, they are created for each flight separately, so createPassenger() or createPax() would be more clear. (pax is common synonym here) \$\endgroup\$ – dzieciou Sep 30 '12 at 8:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.