2

Consider the product class:

class Car {
   private String color;
   private String model_num;
   //getters and setters for the above fields
}

Consider builder class 1:

class CarBuilder1 {
   private Car car;
   //setters for color, model_num
   //build() method returning car obj
}

Now, consider builder class 2:

class CarBuilder2 {
   private Car car;
   private String color;
   private String model_num;
   //setters for color,model_num
   //build() which instantiates car object using fields and returns it
}

We are using 2x the memory of Car class with CarBuilder1, when we can just have one set of variables(already present in the Car object) in CarBuilder2. Yet I see lot of places using CarBuilder1, is there any specific reason for this? Is there any advantage of CarBuilder1 over CarBuilder2 which makes people willing to compromise on the extra storage required?

  • Why do your builders have a reference to a Car that is yet to be created? Or is your Builder1 more like an adapter to add more methods for re-configuring an existing Car? – amon Jul 18 at 13:10
  • @amon both are builders that expose the same interface, but builder 1 takes some liberty about the order of events that is possible only in some limited conditions (i.e. no side effectects, and no memory management). – Christophe Jul 18 at 13:59
  • @Christophe I really don't understand. Neither OP's very terse code snippets, nor your answer. The original object already has setters so the Builder1 seems like pointless boilerplate. If it didn't have setters there would be no way to set its fields because they are private. Your answer asserts that this “is often used” but I've never seen it, although it has some resemblance to the original GoF builder pattern. (To be fair, I don't do much Java, so Builder1 might be more popular over there.) – amon Jul 18 at 16:54
  • @amon I think Bloch's builder and its variant are quite popular in Java. It is indeed not a full-blown GoF builder as we know it, since technically speaking there is not really a step by step building and assembling of parts. Nevertheless it's more than half of the google hits on "Builder", which creates a lot of confusion about what a builder really is. And yes, it requires a lot of boiler-plate. And no, it doesn't ensure that all the mandatory parameters were set. The light variant seems popular because it reduces the boiler-plate to produce the same result, despite object is created too – Christophe Jul 18 at 18:54
  • 1
    @Christophe getters promote weak encapsulation. Setters promote mutable objects. The Josh Bloch Builder Pattern lets you produce an immutable object while simulating named arguments in a language that doesn't have them. Which is why the Joshua Bloch Builder is a Java thing and not a C# thing. I've talked about this before. – candied_orange Jul 19 at 13:24
2

The builder pattern is meant to encapsulate the steps of the creation process:

  • CarBuilder2 is a variant of Joshua Bloch's builder pattern. The main intent is to facilitate the creation of objects that require many parameters in their constructor.

  • CarBuilder1 is a lighter variant of this pattern that is possible if an only if the Car constructor has no side-effect, and parameters are not mandatory at construction and unused objects are destroyed/garbage-collected automatically. If these conditions are met, it provides the same result without the overhead.

Variant 1 is often used, in situations when object expose a lot of setters and don't care about initial state. Since both builders expose the same interface, if Car constructor evolves to make the model and the color required, you could still evolve to CarBuilder2 without any effect on the clients of the builder.

Remark: the extra storage is btw probably not the reason, because once the build is completed, the builder can be destroyed or garbage-collected. The reason is probably to avoid a lot of boiler-plate code

| improve this answer | |
  • Christophe, I understood the first 2 conditions, didn't understand the third condition and its implication. Could you please elaborate "and unused objects are destroyed/garbage-collected automatically" – user3760100 Jul 18 at 14:58
  • @user3760100 yes: in Java you can afford to create the object with the builder, even if build() is never called: When the builder is no longer used, the object that was unnecessarily created gets garbage collected; no impact. In C++, if smart pointers are not used, the creation of the object outside build() might create a resource leak, unless a lot of additional precautions are taken. So the last clause if’s only to generalize the statement beyond garbage-collected languages. If you use java or C# you can ignore it. – Christophe Jul 18 at 15:29
1

Variables consume 64 bits or so, so 8 bytes. Primitives all fit in that, and objects are kept as references to the heap. So, we are talking about 16 bytes or so. I don't think that matters that much.

Also, I just want to mention: Don't use getters/setters, they are bad for you! Setters more so than getters, but they're both bad. Object-orientation is about having behavior in your objects.

| improve this answer | |
  • Agreed that in case its just 64 bits, but in other cases where the instance variables may be the references to objects which have other references, etc, this could be occupying a lot of memory. – user3760100 Jul 18 at 14:43
  • @user3760100 Objects are not deep-copied, unless you explicitly implement that. So it doesn't really matter how deep the hierarchy is, since you only "copy" the top reference into the object you build. In general, you will only use memory when you invoke new (with a couple of exceptions). So unless you do that in your builder, you'll be fine. – Robert Bräutigam Jul 19 at 11:45

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.