Introduction
I want to introduce my web app that can generate "Builder Pattern" source code for Java.
With this tool, you can instantly generate Builder patterns: "Effective Java", "GoF", "Telescoping Constructor", "Lombok", "Mandatory Properties"
https://riversun.github.io/java-builder/
(Made only with HTML / CSS / JS , no server side)
As shown below, if you enter "[Type] [Variable]" in Definition, the generated code will be reflected in real time on the right.
When you have finished entering the code, press Copy to Clipboard to copy the code and paste it into your IDE or editor.
Example of generated code
Definition
Just type like as follows and you can get full source code of Builder pattern.
package com.example
class Person
@Required String name
@Required Integer age
@Required String gender
@Required Integer height
String eyeColor
String hairColor
String hobby
When @NotNull annotation is added to the beginning of the variable definition, it becomes a Required parameter and generates a code to check null when building.
Generated code example "Effective Java"
Person.java
package com.example;
public class Person {
private String name;
private Integer age;
private String gender;
private Integer height;
private String eyeColor;
private String hairColor;
private String hobby;
public static class Builder {
private String name;
private Integer age;
private String gender;
private Integer height;
private String eyeColor;
private String hairColor;
private String hobby;
public Builder() {
}
Builder(String name, Integer age, String gender, Integer height, String eyeColor, String hairColor, String hobby) {
this.name = name;
this.age = age;
this.gender = gender;
this.height = height;
this.eyeColor = eyeColor;
this.hairColor = hairColor;
this.hobby = hobby;
}
public Builder name(String name){
this.name = name;
return Builder.this;
}
public Builder age(Integer age){
this.age = age;
return Builder.this;
}
public Builder gender(String gender){
this.gender = gender;
return Builder.this;
}
public Builder height(Integer height){
this.height = height;
return Builder.this;
}
public Builder eyeColor(String eyeColor){
this.eyeColor = eyeColor;
return Builder.this;
}
public Builder hairColor(String hairColor){
this.hairColor = hairColor;
return Builder.this;
}
public Builder hobby(String hobby){
this.hobby = hobby;
return Builder.this;
}
public Person build() {
if(this.name == null){
throw new NullPointerException("The property \"name\" is null. "
+ "Please set the value by \"name()\". "
+ "The properties \"name\", \"age\", \"gender\" and \"height\" are required.");
}
if(this.age == null){
throw new NullPointerException("The property \"age\" is null. "
+ "Please set the value by \"age()\". "
+ "The properties \"name\", \"age\", \"gender\" and \"height\" are required.");
}
if(this.gender == null){
throw new NullPointerException("The property \"gender\" is null. "
+ "Please set the value by \"gender()\". "
+ "The properties \"name\", \"age\", \"gender\" and \"height\" are required.");
}
if(this.height == null){
throw new NullPointerException("The property \"height\" is null. "
+ "Please set the value by \"height()\". "
+ "The properties \"name\", \"age\", \"gender\" and \"height\" are required.");
}
return new Person(this);
}
}
private Person(Builder builder) {
this.name = builder.name;
this.age = builder.age;
this.gender = builder.gender;
this.height = builder.height;
this.eyeColor = builder.eyeColor;
this.hairColor = builder.hairColor;
this.hobby = builder.hobby;
}
public void doSomething() {
// do something
}
}
You you want to use this builder cod,
just write like this.
AppMain.java
public static void main(String[] args) {
new Person.Builder().name("riversun").age(12).gender("male").height(200).build().doSomething();
}
Generated code example of "GoF"
Person.java
package org.example;
import java.util.ArrayList;
import java.util.List;
public class Person {
private String name;
private int age;
private String sex;
private List<String> hobby = new ArrayList<String>();
public Person() {
}
public Person(String name, int age, String sex, List<String> hobby) {
this.name = name;
this.age = age;
this.sex = sex;
this.hobby = hobby;
}
public Person setName(String name){
this.name = name;
return Person.this;
}
public String getName(){
return this.name;
}
public Person setAge(int age){
this.age = age;
return Person.this;
}
public int getAge(){
return this.age;
}
public Person setSex(String sex){
this.sex = sex;
return Person.this;
}
public String getSex(){
return this.sex;
}
public Person setHobby(List<String> hobby){
this.hobby = hobby;
return Person.this;
}
public Person addHobby(String hobby){
this.hobby.add(hobby);
return Person.this;
}
public List<String> getHobby(){
return this.hobby;
}
public void doSomething(){
System.out.println("Person's properties");
System.out.println("name="+name);
System.out.println("age="+age);
System.out.println("sex="+sex);
System.out.println("hobby="+hobby);
}
}
Builder.java
package org.example;
import java.util.List;
public interface Builder {
public void name(String name);
public void age(int age);
public void sex(String sex);
public void hobby(List<String> hobby);
Person getResult();
}
PersonBuilder.java
package org.example;
import java.util.List;
public class PersonBuilder implements Builder {
private Person person;
public PersonBuilder() {
this.person = new Person();
}
@Override
public void name(String name) {
this.person.setName(name);
}
@Override
public void age(int age) {
this.person.setAge(age);
}
@Override
public void sex(String sex) {
this.person.setSex(sex);
}
@Override
public void hobby(List<String> hobby) {
this.person.setHobby(hobby);
}
@Override
public Person getResult() {
if(this.person.getName() == null){
throw new NullPointerException("The property \"name\" is null. "
+ "Please set the value by \"builder.name()\" at Director class. "
+ "The property \"name\" is required.");
}
return this.person;
}
}
Director.java
package org.example;
public class Director {
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
public void construct() {
builder.name("something"); // required property
builder.age(0); // optional property
builder.sex("something"); // optional property
//builder.hobby(new ArrayList<>());
}
}
AppMain.java
package org.example;
public class AppMain {
public static void main(String[] args) {
Builder builder = new PersonBuilder();
Director director = new Director(builder);
director.construct();
Person person = builder.getResult();
person.doSomething();
}
}
Generated source code of "Telescoping Constructor"
Person.java
package org.example;
import java.util.ArrayList;
import java.util.List;
public class Person {
private String name;
private int age;
private String sex;
private List<String> hobby = new ArrayList<String>();
public Person() {
}
public Person(String name, int age, String sex, List<String> hobby) {
this.name = name;
this.age = age;
this.sex = sex;
this.hobby = hobby;
}
public Person setName(String name) {
this.name = name;
return Person.this;
}
public String getName() {
return this.name;
}
public Person setAge(int age) {
this.age = age;
return Person.this;
}
public int getAge() {
return this.age;
}
public Person setSex(String sex) {
this.sex = sex;
return Person.this;
}
public String getSex() {
return this.sex;
}
public Person setHobby(List<String> hobby) {
this.hobby = hobby;
return Person.this;
}
public Person addHobby(String hobby){
this.hobby.add(hobby);
return Person.this;
}
public List<String> getHobby() {
return this.hobby;
}
}
Generated source code of "Lombok"
Person.java
package org.example;
import java.util.ArrayList;
import java.util.List;
public class Person {
private String name;
private int age;
private String sex;
private List<String> hobby;
Person(String name, int age, String sex, List<String> hobby) {
if(name == null){
throw new NullPointerException("The property \"name\" is null. "
+ "Please set the value by \"name()\". "
+ "The property \"name\" is required.");
}
this.name = name;
this.age = age;
this.sex = sex;
this.hobby = hobby;
}
public static PersonBuilder builder(){
return new PersonBuilder();
}
public static class PersonBuilder {
private String name;
private int age;
private String sex;
private List<String> hobby = new ArrayList<String>();
PersonBuilder() {
}
public PersonBuilder name(String name){
this.name = name;
return PersonBuilder.this;
}
public PersonBuilder age(int age){
this.age = age;
return PersonBuilder.this;
}
public PersonBuilder sex(String sex){
this.sex = sex;
return PersonBuilder.this;
}
public PersonBuilder hobby(List<String> hobby){
this.hobby = hobby;
return PersonBuilder.this;
}
public PersonBuilder addHobby(String hobby){
this.hobby.add(hobby);
return PersonBuilder.this;
}
public Person build() {
return new Person(this.name, this.age, this.sex, this.hobby);
}
@Override
public String toString() {
return "Person.PersonBuilder(name=" + this.name + ", age=" + this.age + ", sex=" + this.sex + ", hobby=" + this.hobby + ")";
}
}
@Override
public String toString() {
return "Person(name=" + this.name + ", age=" + this.age + ", sex=" + this.sex + ", hobby=" + this.hobby + ")";
}
public void doSomething() {
// do something
}
}
You you want to use this builder cod,
just write like this.
AppMain.java
public class AppMain {
public static void main(String[] args) {
System.out.println(Person.builder().name("Tom").age(12).sex("male").build());
}
}
Generated source code of "Mandatory Properties"
It is a Builder pattern that can set mandatory properties in the correct order.
Person.java
package com.example;
import java.util.Optional;
public class Person {
private final String name;// Required
private final Integer age;// Required
private final String gender;// Required
private final Integer height;// Required
private final Optional<String> eyeColor;// Optional(use Optional<String>)
private final Optional<String> hairColor;// Optional(use Optional<String>)
private final Optional<String> hobby;// Optional(use Optional<String>)
Person(Builder.Builder1 builder) {
this.name = builder.name;
this.age = builder.age;
this.gender = builder.gender;
this.height = builder.height;
this.eyeColor = builder.eyeColor;
this.hairColor = builder.hairColor;
this.hobby = builder.hobby;
}
public static Builder builder() {
return new Builder();
}
public static final class Builder {
public Builder1 name(String name) {
return new Builder1(name);
}
public static final class Builder1 {
final String name;
Integer age;
String gender;
Integer height;
Optional<String> eyeColor;
Optional<String> hairColor;
Optional<String> hobby;
private Builder1(String name) {
this.name = name;
}
public Builder2 age(Integer age) {
this.age = age;
return new Builder2(Builder1.this);
}
}
public static final class Builder2 {
final Builder1 builder;
private Builder2(Builder1 builder) {
this.builder = builder;
}
public Builder3 gender(String gender) {
this.builder.gender = gender;
return new Builder3(this.builder);
}
}
public static final class Builder3 {
final Builder1 builder;
private Builder3(Builder1 builder) {
this.builder = builder;
}
public Builder4 height(Integer height) {
this.builder.height = height;
return new Builder4(this.builder);
}
}
public static final class Builder4 {
final Builder1 builder;
private Builder4(Builder1 builder) {
this.builder = builder;
}
public Builder4 eyeColor(String eyeColor){
this.builder.eyeColor = Optional.of(eyeColor);
return this;
}
public Builder4 hairColor(String hairColor){
this.builder.hairColor = Optional.of(hairColor);
return this;
}
public Builder4 hobby(String hobby){
this.builder.hobby = Optional.of(hobby);
return this;
}
public Person build() {
return new Person(this.builder);
}
}
}
public String name() {
return this.name;
}
public Integer age() {
return this.age;
}
public String gender() {
return this.gender;
}
public Integer height() {
return this.height;
}
public Optional<String> eyeColor() {
return this.eyeColor;
}
public Optional<String> hairColor() {
return this.hairColor;
}
public Optional<String> hobby() {
return this.hobby;
}
@Override
public String toString() {
return "Person(name=" + this.name + ", age=" + this.age + ", gender=" + this.gender + ", height=" + this.height + ", eyeColor=" + this.eyeColor + ", hairColor=" + this.hairColor + ", hobby=" + this.hobby + ")";
}
public void doSomething() {
// do something
}
}
Generator Tips
The following ideas have been put in place with the aim of typical code output where the generated code can be used as is as possible without setting the details.
Effective Java Template
- For type of List<> like as
List<String> hobby;
, generate code initialized withnew ArrayList();
- Also, in the case of
List
, generate a method likeaddHobby(String hobby)
so that items can be added individually.
GoF Template
- GoF's Builder pattern is more complicated than other patterns in construction, generate main class named
AppMain.java
. - Added temporary code to
construct()
method of Director.java.
public void construct() {
builder.name("something");
builder.age(0);
builder.sex("something");
//builder.hobby(new ArrayList<>());
}
I hope it helps.
Top comments (6)
Interesting tool. It's a pain to write these things by hand.
However, a note (or you may call it a feature request): with these templates, there is no check done by the builder that all or a mandatory subset of parameters have been given a value. You could use
Optional<>
to encode arguments that may not be necessary to construct the object; after all, one of the use cases of the Builder pattern is managing construction-time validation of the constructor arguments.Another idea: there is at least another implementation style for the pattern; the one in which each step is encoded in a Class (or better, an Interface) so that the user must follow a precise call sequence in building the object. I.e:
The
Point
class can only be instantiated in this way:This style may be useful when you want to thightly constrain how an object is created.
Thanks for your helpful comments.
I added @NotNull annotation to specify required properties and not required ones.
They are used for null checks in build methods.
What do you think?
The tool works with @Required and @NonNull as aliases for @NotNull as well.
Your "Another idea" is also interesting as a design that forces the coder to set the required parameters in order.
Best.
I like the idea of using Optional but I thought it caused problems when declaring a field as Optional. Was this something in Effective Java? I'm not so sure it is an issue but it is possible to wrap the object in the getter instead.
I supported the source code generation of the Builder pattern that can forcibly set the mandatory property in the correct order.
Here is an example code auto generated with my tool.(select Mandatory Properties)
Thanks for providing this nice tool @riversun
One bug totally unrelated to the builder generation itself : the canonical URL used by riversun.github.io/java-builder/ is wrong and links to getbootstrap site.
PS : I could not find your github repo for this project to fill an issue/PR