Paan's cave

Thoughts about tech & life


Let Me Tell You About Builders

I love builders.

I really like builders. There is just something satisfying about them that I love.

But there are many ways for you to, ehem, build , a builder. I’ve seen them simple, hacky, over engineered and all the shades in between and I love them all.

If you ever had a friend that can’t stop showing you pictures of their kids, pets or gundam collection, then you know to brace yourselves. Cause, just like them, I am going to show you all the builders.

Make yourself comfortable, this is going to take a while.

Duck builder. I’m just fluent

At the most basic level, we have the ‘setter masquerading as builder’. Here you basically have a bunch of setters that returns self to facilitate the fluent builder-like syntax.

Let’s look at that. Let’s say we have this object

public final class UserSearchCriteria {

    private String nameContains;
    private Integer minAge;
    private Integer maxAge;
    private String city;
    private Boolean verified;
    private Boolean includeInactive = false;

    public UserSearchCriteria nameContains(String value) {
        this.nameContains = value;
        return this;
    }

    public UserSearchCriteria minAge(Integer value) {
        this.minAge = value;
        return this;
    }

    public UserSearchCriteria maxAge(Integer value) {
        this.maxAge = value;
        return this;
    }

    public UserSearchCriteria city(String value) {
        this.city = value;
        return this;
    }

    public UserSearchCriteria verified(Boolean value) {
        this.verified = value;
        return this;
    }

    public UserSearchCriteria includeInactive() {
        this.includeInactive = true;
        return this;
    }

    public UserSearchCriteria excludeInactive() {
        this.includeInactive = false;
        return this;
    }
}

GOF purist is probly having a stroke right now. But let’s be real, this is fine. The object is not expensive to mutate. In most cases you ever only need to set a few fields different from the default.

The advantage of this gives you is the fluent setter. Gives you a nice syntax at the call site.

UserSearchCriteria activeUsersInLA = new UserSearchCriteria()
            .city("Los Angeles")
            .excludeInactive();

If you never work with fluent syntax, you are really missing out. It just feels like magic to just keep pressing period to pull up the autocomplete , and keep doing that for like 7 things.

It makes it way easier to read if you need to build a list of a few of these search criteria as presets. There are other ways to do this, there always is another way to do anything. But this is a love letter to builders, so shut up. Not every code is perfect by your arbitrary standards.

Your run of the mill builders

If we move up a few streets away from the builder ghetto, we start to see something more.. textbook. Here in the suburb you will find the soccer mom of builders. The actual builder. This doesn’t need much introduction, just copy paste the GOF code snippet and change the name of the class and fields to fit your domain and there you have it. A builder pattern.

The standard builder pattern is elegant in it’s own simple way. You have a clean separation of concern between creating something and being something. There are a few other advantage of builder pattern other than the standard, often touted, benefits of builders, like for example, not having 54 constructor signatures.

There are architectural reasons for builder, you want immutable objects, etc etc. Go read the design pattern book.

But for me, I mostly love it for the ergonomics around it. Using a builder is just fun. You don’t need to care about order of the constructor parameter, you don’t need to know which parameter is final needs to be passed in the constructor and which can be set later. Good luck finding out if which parameter is which for a SearchCriteria(bool,bool,bool,bool,bool,int,bool). May the programming gods protect you if you need to set the 5th parameter of a constructor but need to make sure all others stays at default.

Not that you can’t go wrong with builders, you just mostly don’t do wrong by default.

Gotta use that CS degree, I guess

Now we start to get into my favourite part, the slightly more engineered builders. Many would say it’s probably over engineered. I don’t believe there is such a thing.

First there is the many ways you define where the builder even is. You have the straight forward its just another class.

public class House{}
public class HouseBuilder{}

There’s the classic Builder inner class.

public class House{
    public static HouseBuilder builder(){}
    class HouseBuilder{}
}

//you call it like this
House twoBedroomHouse = House.builder()
                .withBedroom(2)
                .withToilet(1)
                .build();

I like it cause I dont need to hunt down the builder’s name. Most of the time its obvious, HouseBuilder.house() is probly corrrect 90% of the time. But sometime you get HouseAndApartmentBuilder.House() (messy single dev project) or BuildingBuilder.house() (I’ve got so many builders that I have to group them) or Builders.HouseBuilder.house() (fun with statics) or DwellingBuilder (it was later renamed to house but the builder never caught up), or my favourite AbstractBuilderFactory.Builders.get().house() (Enterprise SaaS)

With this convention there is no question, if the class you want to instantiate has a builder it’s the inner class of the class itself and its named builder. You also get code locality, everbody is happy.

Then there’s the slightly sneaky “I look like a constructor but really I’m just a static method” builder

public class House{
    public static HouseBuilder House(){}
    class HouseBuilder{}
}

//call site looks like this
import House::*;

House twoBedroomHouse = House()
                .withBedroom(2)
                .withToilet(1)
                .build();

Combine it with static imports and you have this constructor looking syntax that’s actually a static call to a builder. Sneaky.

When you already read GoF cover to cover but want something more

There is a form of builders that use what people call “F bounded polymorphism”, I heard people call it recursive generics.

It has the mind bending class declaration that looks something like this

abstract class ClassBuilder<T extends ClassBuilder<T>> {
    protected abstract T self();

    public T setFoo(int val) {
        self.foo = val;
        return self();
    }
}

public class MyClass extends ClassBuilder<MyClass>

This needs a little explanation.

It solves the problem of having a builders on a base abstract class. And subclasses being able to have builders methods to the base fields but also able to access and return subclass fields. It’s really nice to use.

It sounds like your typical abstraction hell, but imagine you have a base class animal, And you have subclasses for fish or birds or mammals. If you want builders for your bird, it now has to do reimplement every setter that you have in your base class. Because a call to setLegs in the animal builder will return you an animal builder instead of a bird builder. And just like that, you lost your fluent syntax.

Those type bounds will ensure that even if you call a setter that is inherited you will always return the subclassed behaviour.

Bird big = new Bird()
    .setLegs(2)      
    .setWings(2)     
    .build();

You get that from this:

class Animal {

    final String name;
    final int legs;

    protected Animal(Builder<?> b) {
        this.name = b.name;
    }

    static abstract class Builder<T extends Builder<T>> {
        private String name;

        public T setName(String name) {
            this.name = name;
            return self();
        }
        public T setLegs(int legs) {
            this.legs = legs;
            return self();
        }

        protected abstract T self();

        abstract Animal build();
    }
}

class Bird extends Animal {

    final int wings;

    private Bird(Builder b) {
        super(b);
        this.wings = b.wings;
    }

    public static class Builder extends Animal.Builder<Builder> {
        private int wings;

        public Builder setWings(int wings) {
            this.wings = wings;
            return this;
        }

        @Override
        protected Builder self() {
            return this;
        }

        @Override
        public Bird build() {
            return new Bird(this);
        }
    }
}

This one is more of a case of nice to extend than it is nice to call. If you extend a class that already has a builder, you just need to extend the builder with any new fields that the builder needs to set, then everything else is magically inherited by the subclassed builder.

It truly is magical.

My god its full of builders

Here’s some other variations of builders.

What if you have a field that is an object.

Order order = OrderBuilder
    .order("cust-001")
    .withItem(new Item("SKU-1"))
    .withItem(new Item("SKU-2"))
    .build();

Not really nice is it. You have a builder that in turn make you instantiate an instance manually. Really kills the vibe. Also if you have a bunch of fields that you need to set on those instance, you end up back in telescoping constructor land. So you builderize that.

Order order = OrderBuilder
    .order("cust-001")
    .withItem(ItemBuilder.item("SKU-1").withQuantity(2).build())
    .withItem(ItemBuilder.item("SKU-2").withQuantity(4).build())
    .build();

That seems ok, but it starts to look really messy if you have many fields and god forbids if Item also needs an object field. Then you call a new builder in the field of the builder that is in your first builder.

“Bro I heard you like builders”, I hear you say.

Yes I do but there is a better way to live.

You could instead do this:

Order order = OrderBuilder
    .order("cust-123")
        .item("SKU-1")
            .qty(2)
            .priceCents(1599)
        .end()
        .item("SKU-2")
            .qty(1)
            .priceCents(4999)
        .end()
    .build();

Behold the glory of the nested fluent builder. Bow before the beauty and grace. You can’t see from the code but using this is so much fun. You press period and you get bunch of methods suggestion for order. Then you pick order, fill in the parameters, then press period again. Here’s where the magic happens, its now all the fields on the item! I love it.

This is how it works

public class OrderBuilder {
    public static OrderBuilder order(String customerId) {
        return new OrderBuilder(customerId);
    }

    // ---- output model ----
    public static class Order {
        final String customerId;
        final List<Item> items;
    }

    public static class Item {
        final String sku;
        int qty;
        int priceCents;
    }

    // ---- root builder ----
    private final String customerId;
    private final List<Item> items = new ArrayList<>();

    public ItemBuilder item(String sku) {
        Item item = new Item(sku);
        return new ItemBuilder(this, item);
    }

    public Order build() {
        return new Order(customerId, items);
    }

    // ---- child builder ----
    public static class ItemBuilder {
        private final OrderBuilder parent;
        private final Item item;

        public OrderBuilder end() {
            parent.items.add(item);
            return parent;
        }
    }
}

There’s nothing too advanced. It’s just being cheeky and is maintaining internal state and juggling the return type to make the IDE experience so much fun. But the result is that you got this context sensitive fluent builder syntax to build a complex multi layered object.

You can go full unhinged and design a whole DSL like syntax to construct a whole tree of different classes just from a single builder entrypoint.

There’s are my favourites but there are others.

I’m sure there are other variants and and ways to use factory that I didn’t mentioned. I mainly covered stuff that I thought was interesting or novel or clever. And mostly those are related to how the builder API is exposed for others to use, because that for me is where the interesting parts are.

There are many ways to make builders do weird stuff around the build methods, add all kinds of validation or logic. Those are interesting in their own right but not really something that gets me super excited.

Programmers are human too

I knew I loved builders, but before this I couldn’t quite articulate why. But as I was writing this, I started to see a pattern , and I think I am starting to understand why myself.

The thing with builders is that they don’t really change anything about how the object works. It’s mostly about how you think about how to make it easier for your fellow programmers to use your code. And that’s kind of nice.

I know, that’s not all there is to it. there are reasons why people choose builders over say, factories. And there is also people that make apis or libraries where the user is their fellow programmers. So the logic is paper thin.

But I choose to believe that behind the simplicity, elegance or over engineering of it all. That there’s a beating human heart behind it that thought, “Hey, maybe the next guy that uses this will like it if they can use a fluent, polymorphic syntax instead of rawdogging the access to the private members”

And that is why I love builders, in all their infinite shapes and forms.