Search code examples
javaoopdesign-patternsbuilder

Builder pattern build() method output


After reading the Design Patterns book and looking at Builder pattern examples online, I have noticed that there are two different ways of returning the object in the final "build()" method. I was wondering what is the difference between:

Simply returning the maze we have been building

public class MazeBuilderImpl implements MazeBuilder {

private Maze maze;

@Override
public MazeBuilder builder() {
    maze = new Maze();
    return this;
}

// adding rooms, doors etc.

@Override
public Maze build() {
    return maze;
}
}

Passing the maze as a parameter in a constructor and returning that

public class MazeBuilderImpl implements MazeBuilder {

private Maze maze;

@Override
public MazeBuilder builder() {
    maze = new Maze();
    return this;
}

// adding rooms, doors etc.

@Override
public Maze build() {
    return new Maze(maze);
}
}

Solution

  • First one seems worse. The original builder can subsequently be used to modify the state of the built object, since it still holds a reference to it.

    MazeBuilder builder = new MazeBuilderImpl();
    Maze maze = builder.rooms(1).build();
    builder.rooms(2); // modifies the state of maze ...
    

    The copy in the second one prevents that from happening, and allows the builder to be reused.

    Both are bad though, since Maze is implied to be mutable. Builders are best for building immutable objects. It requires re-declaring the fields of Maze as fields of MazeBuilder, i.e. if Maze has a String id field, so will MazeBuilder. Lombok can help with reducing that boilerplate.