Builder And Step Builder Design Pattern In Java

Link Copied To Clipboard !

builder_design_pattern_java_ioecapsule Java

Consider a situation where you need to create class with a constructor having lots of variables inside it. Then the problem arises when one of your team member or some random coder wants to create an object of that class. Because it contains lots of variables and that’s really hard for someone who has to pass values in constructor as remembering order of parameter list is not really a job of programmer.

Builder design pattern is the solution of problem that arises while passing lots of variables in constructor, by providing a method without variables which uses other setters to fill the necessary parameters of constructor.

Consider a simple example class User.

public class User {
	private int id;
	private String name;
	private String email;
	private String username;
	private String password;

	private User(int id, String name, String email, String username, String password) {
		this.id = id;
		this.name = name;
		this.email = email;
		this.username = username;
		this.password = password;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
}

Now, to use Builder Design Pattern, we need a builder class which must be inner class to User because we will need to use the private constructor of User Class in Builder class. Also, we need to make it static because we will have to use it from outer class and not init object from User.

public static class Builder {
		private int id;
		private String name;
		private String email;
		private String username;
		private String password;

		public Builder setId(int id) {
			this.id = id;
			return this;
		}

		public Builder setName(String name) {
			this.name = name;
			return this;
		}

		public Builder setEmail(String email) {
			this.email = email;
			return this;
		}

		public Builder setUsername(String username) {
			this.username = username;
			return this;
		}

		public Builder setPassword(String password) {
			this.password = password;
			return this;
		}

		public User build() {
			if (this.id == 0) {
				throw new NullPointerException("User Must Have An Id !");
			}
			if (this.name == null) {
				throw new NullPointerException("User Must Have Name !");
			}
			if (this.email == null) {
				throw new NullPointerException("User Must Have Email !");
			}
			if (this.username == null) {
				throw new NullPointerException("User Must Have Username !");
			}
			if (this.password == null) {
				throw new NullPointerException("User Must Have Password !");
			}
			return new User(id, name, email, username, password);
		}

	}

Usage Of Builder class i.e. UserBuilder :

public static void main(String[] args) {
		User user = new User.Builder()
				.setId(10)
				.setName("Test Name")
				.setEmail("test@gmail.com")
				.setUsername("testusername")
				.setPassword("testpassword")
				.build();
	}

Still, some improvements in the design pattern improves the quality of code significantly. In the builder we have built so far, one can create an instance without providing full information. For example, you can call build method (which returns the object User) without even calling setId, setName etc. which will throw NullPointerException in our case. Also, the the method we implemented, the order is not provided, meaning that you are blind about the order you should call setters.

To remedy this problem and make the code even better, improved builder design pattern was developed and it is call Step Builder Design Pattern. In this design pattern, you can first set id, then name, then email and so on and finally build the object. To apply this design pattern, you need :

  • Number of interfaces with same number of variables
  • A class implementing these interfaces

Let’s dive into coding :

public static class UserStepBuilder {

		public static IdField newBuilder() {
			return new Steps();
		}

		public static interface IdField {
			NameField setId(int id);
		}

		public static interface NameField {
			EmailField setName(String name);
		}

		public static interface EmailField {
			UsernameField setEmail(String email);
		}

		public static interface UsernameField {
			PasswordField setUsername(String username);
		}

		public static interface PasswordField {
			BuildStep setPassword(String password);
		}

		public static interface BuildStep {
			User build();
		}

		private static class Steps implements IdField, NameField, EmailField, UsernameField, PasswordField, BuildStep {
			private int id;
			private String name;
			private String email;
			private String username;
			private String password;

			@Override
			public User build() {
				if (this.id == 0) {
					throw new NullPointerException("User Must Have An Id !");
				}
				if (this.name == null) {
					throw new NullPointerException("User Must Have Name !");
				}
				if (this.email == null) {
					throw new NullPointerException("User Must Have Email !");
				}
				if (this.username == null) {
					throw new NullPointerException("User Must Have Username !");
				}
				if (this.password == null) {
					throw new NullPointerException("User Must Have Password !");
				}
				return new User(id, name, email, username, password);
			}

			@Override
			public BuildStep setPassword(String password) {
				this.password = password;
				return this;
			}

			@Override
			public PasswordField setUsername(String username) {
				this.username = username;
				return this;
			}

			@Override
			public UsernameField setEmail(String email) {
				this.email = email;
				return this;
			}

			@Override
			public EmailField setName(String name) {
				this.name = name;
				return this;
			}

			@Override
			public NameField setId(int id) {
				this.id = id;
				return this;
			}

		}
	}

Usage Of Step Builder class i.e. UserStepBuilder :

User stepBuilt = User.UserStepBuilder.newBuilder()
				.setId(10)
				.setName("Another Name")
				.setEmail("email@email.com")
				.setUsername("usrname")
				.setPassword("pass")
				.build();

While building an object, you will notice that you have to first call newBuilder(), then you can call setId(int), then you can call setName(String) and so on.

So, coming to an end, here is the full code which contains both Builder and Step Builder functionality.


/**
 * 
 * @author subash
 *
 */
public class User {
	private int id;
	private String name;
	private String email;
	private String username;
	private String password;

	private User(int id, String name, String email, String username, String password) {
		this.id = id;
		this.name = name;
		this.email = email;
		this.username = username;
		this.password = password;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getEmail() {
		return email;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public static class Builder {
		private int id;
		private String name;
		private String email;
		private String username;
		private String password;

		public Builder setId(int id) {
			this.id = id;
			return this;
		}

		public Builder setName(String name) {
			this.name = name;
			return this;
		}

		public Builder setEmail(String email) {
			this.email = email;
			return this;
		}

		public Builder setUsername(String username) {
			this.username = username;
			return this;
		}

		public Builder setPassword(String password) {
			this.password = password;
			return this;
		}

		public User build() {
			if (this.id == 0) {
				throw new NullPointerException("User Must Have An Id !");
			}
			if (this.name == null) {
				throw new NullPointerException("User Must Have Name !");
			}
			if (this.email == null) {
				throw new NullPointerException("User Must Have Email !");
			}
			if (this.username == null) {
				throw new NullPointerException("User Must Have Username !");
			}
			if (this.password == null) {
				throw new NullPointerException("User Must Have Password !");
			}
			return new User(id, name, email, username, password);
		}

	}

	public static class UserStepBuilder {

		public static IdField newBuilder() {
			return new Steps();
		}

		public static interface IdField {
			NameField setId(int id);
		}

		public static interface NameField {
			EmailField setName(String name);
		}

		public static interface EmailField {
			UsernameField setEmail(String email);
		}

		public static interface UsernameField {
			PasswordField setUsername(String username);
		}

		public static interface PasswordField {
			BuildStep setPassword(String password);
		}

		public static interface BuildStep {
			User build();
		}

		private static class Steps implements IdField, NameField, EmailField, UsernameField, PasswordField, BuildStep {
			private int id;
			private String name;
			private String email;
			private String username;
			private String password;

			@Override
			public User build() {
				if (this.id == 0) {
					throw new NullPointerException("User Must Have An Id !");
				}
				if (this.name == null) {
					throw new NullPointerException("User Must Have Name !");
				}
				if (this.email == null) {
					throw new NullPointerException("User Must Have Email !");
				}
				if (this.username == null) {
					throw new NullPointerException("User Must Have Username !");
				}
				if (this.password == null) {
					throw new NullPointerException("User Must Have Password !");
				}
				return new User(id, name, email, username, password);
			}

			@Override
			public BuildStep setPassword(String password) {
				this.password = password;
				return this;
			}

			@Override
			public PasswordField setUsername(String username) {
				this.username = username;
				return this;
			}

			@Override
			public UsernameField setEmail(String email) {
				this.email = email;
				return this;
			}

			@Override
			public EmailField setName(String name) {
				this.name = name;
				return this;
			}

			@Override
			public NameField setId(int id) {
				this.id = id;
				return this;
			}

		}
	}

	public static void main(String[] args) {
		//simple builder
		User user = new User.Builder()
				.setId(10)
				.setName("Test Name")
				.setEmail("test@gmail.com")
				.setUsername("testusername")
				.setPassword("testpassword")
				.build();
		
		//step builder
		User stepBuilt = User.UserStepBuilder.newBuilder()
				.setId(10)
				.setName("Another Name")
				.setEmail("email@email.com")
				.setUsername("usrname")
				.setPassword("pass")
				.build();
	}

}

You May Also Like