DEV Community

Cover image for Fluent Plain Old Object Builder Interface Pattern
Michiel Hendriks
Michiel Hendriks

Posted on

Fluent Plain Old Object Builder Interface Pattern

The title of this article is actually a combination of the following concepts I want to talk about:

  • Plain Old Object
  • Builder Pattern
  • Fluent Interface

These three are quite related and often mixed, but generally not all three at once. So lets talk about what they actually are.

Note: this article uses Java as its base language, but the ideas apply to any object oriented environment.

Plain Old Object

This is an object that just holds data, it does not contain any business logic. In the Java world this type of object is generally called a POJO (Plain Old Java Object).

public class Email {
    String subject;
    String body;
    String sender;
    String receiver;

    public String getSubject() {
        return subject.
    }

    public void setSubject(String aSubject) {
        subject = aSubject;
    }

    ...
}

It just contains fields and the means to read and write those fields. A plain old object should not contain methods which contain so called business logic. For example, it should not have a method like send() to send the email, or delete() to delete the method. And these methods would imply the plain old object has knowledge about communication and persistent storage mechanisms.

It could contain methods can tell some basic about the data is holds. For example a method boolean hasBody() would not be considered business logic. As long as the methods of the plain object only interact with the object itself it would not be considered business logic. (There is always the question when you want to move this logic out of this object and into a business logic oriented object. But that is a different subject.)

Plain Old Objects are generally used for models in model-view-controller setups, for persisted data, and for parameter objects.

Builder Pattern

The builder pattern is a so called creational software design pattern. It is basically a system that allows you to feed it the ingredients and eventually create an instance of an object.

public class EmailBuilder {
    String subject;
    String body;
    String sender;
    String receiver;

    public Email build() {
        Email email = new Email();
        email.setSubject(subject);
        ...
        return email;
    }

    public void withSubject(String aSubject) {
        subject = aSubject;
    }

    ...
}

The main idea behind using a builder is so that you can hide the concrete implementation of an object but still be able to create an instance of it via a public interface.

The builder would for example allow you to make the public interface of the Email object immutable, but still allow the internals of a framework to work with the concrete implementation as a plain old object.

An other use case would be that the builder could return a different concrete implementation.

public class SecureEmailBuilder extends EmailBuilder {
    public Email build() {
        SecureEmail email = new SecureEmail();
        email.setSubject(subject);
        ...
        return email;
    }
}

The calling logic does not know, nor care, what kind of Email subtype it receives when calling the build() method.

Fluent Interface

A fluent interface is not, like the previous two subjects, a type construction. It defines a way for defining the interface of methods.

In a fluent interface methods perform a certain action and return an object to continue with. The end result would be a chained sequence of method calls which would read much like a single sentence.

Commonly fluent methods write a field on an object and return the object itself. But the could also return a different fluent object to continue on.

Fluent Builder

For example, if we make the above mentioned builder a fluent builder we would be able to write the following.

emailBuilder.withSubject("Hello World").withSender("Me").withReceiver("You").build()

The interface of the methods of the builder where changed to return the builder itself.

public class EmailBuilder {
    ...

    public EmailBuilder withSubject(String aSubject) {
        subject = aSubject;
        return this;
    }

    ...
}

You can make this fluent builder less verbose and drop with with prefix (or set if you went for that style), and then you would get:

emailBuilder.subject("Hello World").sender("Me").receiver("You").build()

The last method, build(), is a terminating method as it stops the fluent interface.

This construction is used quite often in frameworks which provide a fluent API. Some good examples in the Java world are jOOQ or Apache Camel. But also the stream API of Java 8 follows this style. Or in the front-end world, jQuery is a good example.

Fluent Plain Old Object

We could also change the Plain Old Object to have a fluent interface.

public class Email {
    ...

    public Email setSubject(String aSubject) {
        subject = aSubject;
        return this;
    }

    ...
}

Now you could write a nice one-liner to create an Email object with all the data you wanted.

new Email().setSubject("Hello World").setSender("Me") ...

But this is not good. The set method no longer bears the interface signature commonly expected from a set method as it no longer returns void. This can break interoperability with a lot of frameworks which expect the set method to not return anything.

To make it play nice we basically need to add a second method for every setter.

public class Email {
    ...

    public void setSubject(String aSubject) {
        subject = aSubject;
    }

    public Email withSubject(String aSubject) {
        setSubject(aSubject);
        return this;
    }

    ...
}

This sadly makes the class a bit more bloated, and also increased the maintainability.

Fluent Object

We can also make our Plain Old Object completely fluent. It would cease to be a plain old object though. But for various use-cases this might be much more interesting.

public class Email {
    String subject;
    String body;
    String sender;
    String receiver;

    public String subject() {
        return subject.
    }

    public Email subject(String aSubject) {
        subject = aSubject;
        return this;
    }

    ...
}

Both the set and get method no longer have a prefix. The no-argument subject() method is the get method, and the single argument subject(String) method is the set method which also returns the Email object so you can chain another method call.

My "Guidelines"

None of the above mentioned constructions are better than the other. There are always trade-offs you need to consider.

Just Plain Old Objects

I generally stick to using Plain Old Objects for data holders which I expect to be persisted in a database, or converted to a static form for transmission (XML, JSON, etc.). For the simple reason that it will have the best framework support.

Parameter Objects

For parameter objects I am generally more inclined to use more fluent objects. But often with some method duplication to not mess with overall style

public class ParamObject {
    ...

    public void setFoo(String aFoo) {
        foo = aFoo;
    }

    public foo(String aFoo) {
        setFoo(aFoo);
        return this;
    }

    public String getFoo() {
        return foo;
    }

    ...
}

This object looks behaves like a plain old object, but also allows it to be used fluently

doSomething(new ParamObject().foo("Hello").bar("world"))

Builders

For builders I generally use the approach as presented for the Fluent Builders using with as a prefix for all the methods which write data. This way there is a clear distinction between methods which perform actions, and methods which just write data.

I do not use set as the prefix, as I would see that as setting a property of the builder, rather than defining a value of the object I am creating.

Components

Components are not covered in this article, but I would like to mention them in my guidelines anyway. An example of a component would be a UI element, for example a button on a form, and the form itself.

Components are also objects, but absolutely no Plain Old Objects. Components obviously have properties. For the set methods I always return the component so I can chain various set actions in a single line.

form.add(new Button("save").setEnabled(false).setType(ButtonType.SUBMIT));

Fluent API

Obviously when you create a fluent API you will go full fluent. This also means that the objects returned in these fluent should be as purely fluent as possible. So no set or get methods for writing fields. Only the terminating call would return a "normal" object.

Discussion (2)

Collapse
tacsiazuma profile image
Krisztian Papp

The main idea behind using a builder is so that you can hide the concrete implementation of an object but still be able to create an instance of it via a public interface.

That's more like the idea behind the factories or creational patterns in general.

"The intent of the Builder design pattern is to separate the construction of a complex object from its representation."
A POJO is not so complex. The only thing you could achieve with this approach is creating immutable DTO's, which failed when you added setters to the class.

Basically, if you want to achieve immutability with the DTO's, then you should create an inner static class Builder, which is mutable. And implement that fluent API on it. When you invoke the build method, you should invoke a private constructor of the outer class which is accessible by the builder and pass the builder instance to it. Then just pass its fields to the outer class instance.

 class Immutable {
    private final String whatever;

    private Immutable(Builder builder) {
        this.whatever = builder.whatever;
    }

    public static class Builder {
        private String whatever;

        public Builder withWhatever(String whatever) {
           this.whatever = whatever;
           return this;
        }
        public Immutable build() {
           return new Immutable(this);
        }

    }

  }

In this way, you aren't violating any checkstyle rule, got rid of the setters, could make those fields in the outer class final. But this isn't really a builder, because you should think about default values, validation, different implementations and so on.

Collapse
lysofdev profile image
Esteban Hernández

Quality post! Where would you place, under these guidelines, the initialization of of sensitive default values?