3

Shoutout to David Arno for teaching me about the builder design pattern via this thread!

I have since used that pattern althroughout the code base to abstract out creating models from data stores, ready to use in the test cases. See the link to the other question for example on how we're using it.

However, model creation is not entirely abstracted out. On special-scenario test cases and the sanity test cases, we are still creating models by hitting their constructors directly. For example, in one of the sanity tests, we create the model like:

ContractModel model = new ContractModel(SMDDateUtils.toDate('08-16-2021'), 
    SMDDateUtils.toDate('08-16-2021'), 
    3, 
    'dummyContractLabelName', 
    Frequency.QUARTERLY, 
    45, 
    5, 
    true, 
    26, 
    SMDConstants.URL,
    Signatory.PHYSICIAN, 
    new BankDetailModel(3)
    )

What a nightmare! (What if we wanted to add to, remove from, or change up that model class, for whatever reason...?!)

We do have a ContractBuilder already, but its job is to create ContractModels from the contracts data store.

Upon delving into the builder design pattern, I know that it is most commonly implemented another way. This have the benefit of making that happy-path ContractModel creation like:

ContractModel model = ContractModel.builder()
   .signDate(SMDDateUtils.toDate('08-16-2021'))
   .startDate(SMDDateUtils.toDate('08-16-2021'))
   //...
   .build()

which would be way the hell easier to instantly understand and maintain. Even better, there's convenient third-party library for that.

The question is: would it be a code smell to have both types of builder going on (or even for the first to use the second)?

3
  • 1
    "We do have a ContractBuilder already, but its job is to create ContractModels from the contracts data store." - could you please give an example or scetch how that "builder" looks like? It sounds more like you have a repository there, not (or not just) a builder.
    – Doc Brown
    Nov 11, 2021 at 7:27
  • Example of that "builder" can be found in the original question that was linked. Nov 11, 2021 at 8:12
  • This is for model creation in Katalon Studio, a testing framework that is powered by Groovylang. Nov 11, 2021 at 8:28

2 Answers 2

2

Your question does not really contain "two different implementations of the same builder pattern". Instead, it describes two common, but actually different uses of the name "Builder pattern", one more general and one very specific:

  • ContractBuilder is more or less a variant of the classical GoF Builder pattern. This usage of the name "builder pattern" fits to almost any case where a complex object construction is moved away from a constructor into a separate class. In this sense, factory classes can be seen also as builders. And when it comes to construction of objects from a datastore, often repository classes can take the function of a builder as well (though their main purpose is usually a different one). This builder pattern is mostly programming language agnostic.

  • ContractModel.builder(), however, is a way more specific kind of builder, also known as Joshua Bloch’s Builder design pattern. It is a workaround for a missing programming language feature in Java (named parameters), it has a very specific standard implementation, especially in Java, and is usually not required in programming languages which support named parameters. Hence it is more a Java idiom.

Since both patterns solve different problems (or at least work at different levels of abstraction), there is definitely nothing wrong in using them both in a single program, and it can make perfectly sense to let a ContractBuilder use the fluent interface of ContractModel.builder(). I would, however, consider not to name both implementation classes builder, which can help to avoid some confusion. Maybe rename your ContractBuilder to something like ContractCreator. Maybe you can come up with a completely different name.

The GoF design patterns do not get better or worse by sticking religously to the names and terms which were used in the original GoF book - when it helps to make things clearer, just feel free to pick more distinctive names.

1
  • 1
    Thank you so much for looking into this, and your in-depth explanation of this! I've been racking my head against this since last night! I think I'll hold off on using Joshua Bloch's Builder design pattern, especially since there are bigger fish to fry at work right now, and the manual creation of models for specific testing scenarios is proabably best left to some ContractFactory, for example. Nov 12, 2021 at 3:37
-6

You have two ways to create your model. One looks more readable. Use the one that is more readable. The only code calling the constructor directly is the builder code.

And strongly avoid the term “code smell”. It’s the kind of language that Göbbels invented. It’s a direct ad hominem attack against the author of the code.

(For the constructor to call the builder code, if that’s what you are asking, would be difficult and pointless, it doesn’t give you any benefits and is technically difficult).

PS. Every third party library is a long term debt. Think hard whether you want to take it on.

4
  • 5
    Can we keep the nazis out of a discussion of software design patterns?
    – JacquesB
    Nov 11, 2021 at 7:49
  • 2
    "Every third party library is a long term debt" -- nowhere near as much debt as re-inventing the wheel for an already-solved problem for which all the expensive and time-consuming effort has been done by someone else, including thorough testing, peer-review and support from a very large community. A "Not Invented Here" mentality has real-world implications for the long-term maintainability and financial viability of a project. There are rare occasions where it's valid to avoid 3rd party libraries, but in 99% of projects, the long-term costs of this mentality are vastly greater. Nov 11, 2021 at 7:50
  • Being tightly coupled to libraries, especially for simple things, can create a world of problems and make your code difficult to maintain, much less for someone else who is just seeing your code for the first time, or for you later on in the future. That being said, libraries do have their place in avoiding an avalanche of redundant refactoring, especially if their end use case is for something small compared to the amount of work that's needed to do what those libraries are doing manually. Nov 11, 2021 at 8:25
  • What you wrote in first paragraph would only apply if both implementations would be attempts to solve the same problem. But this is not the case here.
    – Doc Brown
    Nov 11, 2021 at 21:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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