Builders of test objects for hierarchies of classes

Builders of test objects for hierarchies of classes

So you need to implement builders of test objects for hierarchies of classes, eh? I went down that road a few years ago, and I still haven’t found a completely satisfying solution… But I’m getting ahead of myself. Let me set the context…

The context

As a diligent developer you decide to write a whole bunch of tests for that application you’re working on. For that purpose, you write test object builders. Those builders will help you to quickly build domain objects for your tests. Great!
Unfortunately those domain objects are implemented in a hierarchical way, using inheritance. Now, we could discuss how multiple layers of inheritance make a code base smell bad… But that’s not the point of this post. You’re stuck with this hierarchy of classes:

class Child {
    public int teeth;
}

class Dad extends Child {
    public boolean married;
}

class Grandpa extends Dad {
    public int wrinkles;
}

Since you don’t want to duplicate the same setter methods in each builder, you decide to go for a hierarchy of builders as follows:

class ChildBuilder {
    protected int teeth;
    ChildBuilder teeth(int teeth) {
        this.teeth = teeth;
        return this;
    }
    Child build() {
        Child child = new Child();
        child.teeth = this.teeth;
        return child;
    }
}

class DadBuilder extends ChildBuilder {
    protected boolean married;
    DadBuilder married(boolean married) {
        this.married = married;
        return this;
    }
    Dad build() {
        Dad dad = new Dad();
        dad.teeth = this.teeth;
        dad.married = this.married;
        return dad;
    }
}

class GrandpaBuilder extends DadBuilder {
    protected int wrinkles;
    GrandpaBuilder wrinkles(int wrinkles) {
        this.wrinkles = wrinkles;
        return this;
    }
    Grandpa build() {
        Grandpa grandpa = new Grandpa();
        grandpa.teeth = this.teeth;
        grandpa.married = this.married;
        grandpa.wrinkles = this.wrinkles;
        return grandpa;
    }
}

Each builder inherits from the previous. In particular, each builder inherits from a parent in the same way that the Dad object inherits from the Child, and the Grandpa from the Dad. That way you can capitalize on the parent builders to build the more complex Dad or Grandpa instances.

That looks just fine, right? But once you write your first test, you get in trouble:

@Test
public void shouldTestWhatATestShouldTest() {
    Child child = new ChildBuilder().teeth(28).build();
    
    Dad dad = new DadBuilder().teeth(32).married(true).build();
    dad = new DadBuilder().married(true).teeth(32).build();
}

The issue

The problem appears when you concatenate methods from the Dad builder with a method from the Child builder. Under those circumstances, the compiler will complain. That’s because, the type of object returned by “teeth()” will be a “ChildBuilder“, so it won’t recognize the “married()” method. Also, even when swapping the order of the calls, one still ends with a Child object instead of a Dad object aftercalling the final “build()” method!

So what now?

Well, we could attempt to use the “getThis() trick” to obtain the most “extended” builder. Indeed, we want to obtain a “DadBuilder” object type when calling “teeth()” method in the “ChildBuilder“. So, by delegating the method’s return this;to a “getThis()” method and overriding in the builder sub-classes, we could ensure that “ChildBuilder.teeth()” calls the overridden “getThis()” in “DadBuilder“.

See for yourself:

class ChildBuilder {
    protected int teeth;
    ChildBuilder teeth(int teeth) {
        this.teeth = teeth;
        return getThis();
    }
    Child build() {
        Child child = new Child();
        child.teeth = this.teeth;
        return child;
    }
    protected ChildBuilder getThis() {
        return this;
    }
}

class DadBuilder extends ChildBuilder {
    protected boolean married;
    DadBuilder married(boolean married) {
        this.married = married;
        return getThis();
    }
    Dad build() {
        Dad dad = new Dad();
        dad.teeth = this.teeth;
        dad.married = this.married;
        return dad;
    }
    protected DadBuilder getThis() {
        return this;
    }
}

class GrandpaBuilder extends DadBuilder {
    protected int wrinkles;
    GrandpaBuilder wrinkles(int wrinkles) {
        this.wrinkles = wrinkles;
        return getThis();
    }
    Grandpa build() {
        Grandpa grandpa = new Grandpa();
        grandpa.teeth = this.teeth;
        grandpa.married = this.married;
        grandpa.wrinkles = this.wrinkles;
        return grandpa;
    }
    protected GrandpaBuilder getThis() {
        return this;
    }
}

So, problem solved then? Huh, not yet. The overriding of “getThis()” in each builder does not fix the type returned by the “married()” and “wrinkles()” methods.

Therefore we must bring in generics (insert famous dramatic look meme here).

The curiously-recurring template pattern

Enters the curiously-recurring template pattern, applied by following this Stack Overflow post.

abstract class ChildBuilder<T extends ChildBuilder<T>> {
    protected int teeth;
    T teeth(int teeth) {
        this.teeth = teeth;
        return getThis();
    }
    Child build() {
        Child child = new Child();
        child.teeth = this.teeth;
        return child;
    }
    protected T getThis() {
        return (T) this;
    }
}

abstract class DadBuilder<T extends ChildBuilder<T>> extends ChildBuilder<T> {
    protected boolean married;
    T married(boolean married) {
        this.married = married;
        return getThis();
    }
    Dad build() {
        Dad dad = new Dad();
        dad.teeth = this.teeth;
        dad.married = this.married;
        return dad;
    }
    protected T getThis() {
        return (T) this;
    }
}

class GrandpaBuilder extends DadBuilder<GrandpaBuilder> {
    protected int wrinkles;
    GrandpaBuilder wrinkles(int wrinkles) {
        this.wrinkles = wrinkles;
        return getThis();
    }
    Grandpa build() {
        Grandpa grandpa = new Grandpa();
        grandpa.teeth = this.teeth;
        grandpa.married = this.married;
        grandpa.wrinkles = this.wrinkles;
        return grandpa;
    }
    protected GrandpaBuilder getThis() {
        return this;
    }
}

The pattern makes a very clever usage of generics (insert famous dramatic… no wait, I’ve done that joke already) so that it avoids directly referring to the parent builder (aka “GrandpaBuilder“). Now “getThis()” always returns a GrandpaBuilder, therefore the concatenation of any method in any builder is possible. Try it for yourself!

@Test
public void shouldTestWhatATestShouldTest() {
    Child child = new GrandpaBuilder().teeth(28).build();

    Dad dad = new GrandpaBuilder().teeth(32).married(true).build();
    dad = new GrandpaBuilder().married(true).teeth(32).build();

    Grandpa grandpa = new GrandpaBuilder().wrinkles(294).married(true).teeth(25).build();
    grandpa = new GrandpaBuilder().teeth(25).wrinkles(294).married(true).build();
    grandpa = new GrandpaBuilder().married(true).teeth(25).wrinkles(294).build();
}

Are we done then? Uh, not yet…

Indeed, this approach comes with a few shortcomings. For one, you must make the parent builders abstract so that the “getThis()” trick works. Hence you cannot instantiate a “ChildBuilder“: you must instantiate a “GrandpaBuilder” to build a Child. And unfortunately that also exposes all the building methods of “DadBuilder” and “GrandpaBuilder“, which is not what we want.
Most importantly you do not build Child instances, but Grandpa objects referred as Child! See for yourself:

@Test
public void shouldTestWhatATestShouldTest() {
    
    ...

    assertEquals(Child.class, child.getClass());
    assertEquals(Dad.class, dad.getClass());
    assertEquals(Grandpa.class, grandpa.getClass());
}

Of course we could replace those “build()” methods with explicit ones, such as “buildChild()“, “buildDad()” etc. However I couldn’t help but feel there might be a better solution.

Builders of test objects for hierarchies of classes

Let’s reconsider the problem at hand: we need builders to produce test objects for a hierarchy of classes. However we do not need our builders to follow the same hierarchy!

So let’s bring in a “CommonBuilder” that gathers all of the hierarchy’s properties. Each builder could therefore extend that “CommonBuilder“. At the same time we could leave the “build()” methods in their respective builders, so we can be sure of what will be built at the end.

abstract class CommonBuilder<T> {
    protected int teeth;
    protected boolean married;
    protected int wrinkles;
    T teeth(int teeth) {
        this.teeth = teeth;
        return (T) this;
    }
    T married(boolean married) {
        this.married = married;
        return (T) this;
    }
    T wrinkles(int wrinkles) {
        this.wrinkles = wrinkles;
        return (T) this;
    }
}

class ChildBuilder extends CommonBuilder<ChildBuilder> {
    Child build() {
        Child child = new Child();
        child.teeth = this.teeth;
        return child;
    }
}

class DadBuilder extends CommonBuilder<DadBuilder> {
    Dad build() {
        Dad dad = new Dad();
        dad.teeth = this.teeth;
        dad.married = this.married;
        return dad;
    }
}

class GrandpaBuilder extends CommonBuilder<GrandpaBuilder> {
    Grandpa build() {
        Grandpa grandpa = new Grandpa();
        grandpa.teeth = this.teeth;
        grandpa.married = this.married;
        grandpa.wrinkles = this.wrinkles;
        return grandpa;
    }
}

That looks quite okay, doesn’t it? You still can call “teeth()“, “married()” and “wrinkles()” in any order. However the build methods now return the right kind of instance, as can be verified by running the assertions on the built object types!

But one minor annoyance remains: you can technically make a Child with “wrinkles()… So new ChildBuilder().teeth(28).wrinkles(294).build() , though a bit creepy, is allowed. Of course the built object ignores the specified amount of wrinkles, as it returns a valid Child instance.

Conclusion

As you can see, implementing builders for hierarchies of domain objects is not as simple as it may seem.

Personally, I would avoid hierarchies of domain classes as much as possible. But then one does not always have a choice! In that case, the solution will depend on you hierarchy:

  • If you only need to build one concrete type, and all its parents are actually never instantiated, then you can try your luck with the curiously-recurring template pattern.
  • If you must be able to obtain instances from any of the parents, you may have a smoother ride by falling back on a CommonBuilder” object.

Truly I had a tough time writing this post because, frankly, none of the two solutions fully satisfy me. So if you happen to know of a better one, please be kind and post it in the comments below. I can’t help but feel that, as a Grandpa, I’ll still be scratching my head about this.

Cheers!

[Image by Yury Kim on Pexels]

Leave A Comment

Please be polite. We appreciate that. Your email address will not be published and required fields are marked

This site uses Akismet to reduce spam. Learn how your comment data is processed.