Question

For complex object creation, I am quite fond of Builder Pattern. My 1st question is "Do I need the Builder to be inside my complex class blueprint, or Could I actually have it outside?"

e.g. Are the following two classes as 2 different compilation units okay?

public class MyComplexObj {
   // Have loads of props

}

public class MyComplexObjBuilder {

// builder methods e.g. a(), b(), c() returning this

// finally, call build() and return MyComplexObj(this)
}

My 2nd Question - "Does my complex obj really need to be immutable e.g. no setters, or could I really use Builders for also a mean of "Refactoring" of my object creation?

Was it helpful?

Solution

Do I need the Builder to be inside my complex class blueprint, or Could I actually have it outside?

No, you don't need it to be inside. I personally dislike using classes as namespaces and always put a builder in its own file. But others have a different view on this to me. There's no right answer here, it's very much a case of personal style and taste.

As 9000 points out in their answer, putting it inside the class can have the advantage that it can access private members of the class to be built. Again I don't see this as an advantage but others clearly do.

Does my complex obj really need to be immutable e.g. no setters, or could I really use Builders for also a mean of "Refactoring" of my object creation?

These aren't actually contradictory requirements. Using the "wither" pattern, you can use a builder class to take an immutable object and construct a new immutable object from it with slightly different values.

As to whether to make the complex object immutable, that's a balancing act between idealism and pragmatism. Ideally, all classes should be immutable. Immutable objects have big advantages in terms of code reliability and are generally easier to reason about and debug. But in practice, a 100% immutable app would be pretty useless in all but edge cases. And immutability can have performance implications. So using pragmatism by allowing some mutability is essential. If you can make the complex object immutable, then great. If you need it to be mutable though, then don't sweat it: make it mutable.

OTHER TIPS

Since the class's builder does not make sense in separation from the class it builds, there is a widespread practice to make the builder a nested static class within the target class:

class Foo {
  // ...
  public static Builder builder() { return new Builder(); }
  static class Builder {
    // ...
    public Builder withX(X x) {...}
    public Builder withY(Y y) {...}
    Foo build() { ... }
  }
}

Foo foo = Foo.builder().withX(10).withY(20).build();

Effectively the builder is just namespaced inside its target class. (Often a whole family of builders live within, allowing through the type system to build a class with various subsets of properties but not allowing it to remain incomplete.)

Of course, a builder can live elsewhere, but putting it inside the target class may allow for easier access to non-public methods between them, without exposing such methods to to the outside (at least in Java).

I find such an approach quite clean.

Let me first answer:

Must the built object be immutable?

I personally prefer yes, but let's look at functional languages where everything is immutable. Often there is an exception for object creation. Starting with a large matrix, it would be nice when the data is mutable on construction, and then have a life time as immutable.

And this may hold for an immutable-object builder too: only mutability prior to construction.

I have seen a build having a mutable copy of every object's field under construction, and finally create the actual immutable object. With a huge private constructor.

One could also create an object with non-final fields and provide no setters. Somehow this feels faked, suboptimal. But it saves on having the same fields in the builder, and a private default constructor suffices. Building can happen on an internal object, yet not returned.

Can the builder class be outside?

Indeed an embedded builder class can easily be hard to read, confusing. Having a separate class also obeys the principle separation of concerns.

So I would prefer it for a class with some amount of fields.

The builder method is public, hence its class should be public, the constructor package private.

public class Foo {
    private Foo() { ... }
    public static FooBuilder withBar(Bar bara) { ... }
}
public class FooBuilder {
    FooBuilder() { ... }
}
Licensed under: CC-BY-SA with attribution
scroll top