Ways to create objects in Java with Builder and Factory

Marcos
Javarevisited
Published in
5 min readJan 8, 2022

--

Almost every time I start writing a program I try to define the domain as the first step to be done. This helps me a lot to understand the meaning of an api for example. What does she do? What is she about? Why does it exist?

And then I try to define how the objects will be created. I also pay attention to the word how, thinking about some questions like these:

  • Will anyone be able to give a new object at any time?
  • Will anyone be able to set attributes at any time?
  • Can anyone create any way they want, with null attributes?

In some class I heard an analogy with a car and I carry it with me to this day, it’s like this: I made a car and I’m going to deliver it, I want the person who receives it to reach out, open the engine without knowing what they’re doing, change something or do anything else but drive that car?

That said, I’m going to show you three ways to create objects and their advantages to get away from instantiation and attribute set.

I’m going to pretend I’m programming for a petshop system, so I’m going to think about the little animals that are handled there.

Normally we would have this here:

package com.example.demo.domain;public class Dog {
public String name;
public Integer age;
public String race;
public String sex;
public Double weight;
}

This object would be used as follows:

public class Tests {      
public static void main(String[] args) {
Dog dog = new Dog();
dog.raca = "pitbull";
dog.age = 5;
dog.weight = 10.0;
dog.sex = "M";
}
}

Do not use this way, it has many vulnerabilities and gaps.

I will not comment on this form of use, I will talk about other ways and why they would be more interesting.

The most common way:

For a start I don’t usually leave anything public and I like to define a constructor, so it looks like this:

package com.example.demo.english.domain;import java.time.LocalDate;
import java.time.Period;
public class Dog {
private String name;
private LocalDate birthDate;
private String breed;
private String sex;
private Double weight;
public Dog(String name, LocalDate birthDate, String breed, String sex, Double weight){
this.name = name;
this.birthDate = birthDate;
this.breed = breed;
this.sex = sex;
this.weight = weight;
}
public String getName() {
return name;
}
public Integer getAge() {
return Period.between(this.birthDate, LocalDate.now()).getYears();
}
public String getBreed() {
return breed;
}
public String getSex() {
return sex;
}
public Double getWeight() {
return weight;
}
public void setWeight(Double weight) {
this.weight = weight;
}
}

Now the use of attributes is done by the getters and setters methods and the creation of an object by its constructor passing the parameters like this:

public static void main(String[] args) {
Dog dog = new Dog("Rex", LocalDate.of(2015, 10, 10), "pitbull", "M", 10.0);
dog.getName();
}

Using a factory

It’s relatively simple, we need a class whose only function is to provide the creation of an object:

import java.time.LocalDate;public class DogFactory {    public Dog create(String name, LocalDate birthDate, String breed, String sex, Double weight){
return new Dog(name, birthDate, breed, sex, weight);
}
}

And after that close the Dog class to not allow instantiation:

import java.time.LocalDate;
import java.time.Period;
public class Dog {
private String name;
private LocalDate birthDate;
private String breed;
private String sex;
private Double weight;
public Dog(String name, LocalDate birthDate, String breed, String sex, Double weight){
this.name = name;
this.birthDate = birthDate;
this.breed = breed;
this.sex = sex;
this.weight = weight;
}
public String getName() {
return name;
}
public Integer getAge() {
return Period.between(this.birthDate, LocalDate.now()).getYears();
}
public String getBreed() {
return breed;
}
public String getSex() {
return sex;
}
public Double getWeight() {
return weight;
}
public void setWeight(Double weight) {
this.weight = weight;
}
}

And the use is like that:

import com.example.demo.english.factory.Dog;
import com.example.demo.english.factory.DogFactory;
import java.time.LocalDate;public class FactoryTests { public static void main(String[] args) { DogFactory dogFactory = new DogFactory();
Dog dog = dogFactory.create("Rex", LocalDate.of(2015, 10, 10), "pitbull", "M", 10.0);
}
}

That’s it, we’re using the factory pattern. If someone tries to instantiate in other packages they won’t succeed. Now let’s take a look at the builder.

Using a Builder

Here, things get a little more complicated, but it’s worth it for objects that have a lot of attributes. That’s because passing too many parameters through the constructor can confuse us when creating objects.

Well, I started by creating the DogBuilder class:

import java.time.LocalDate;public class DogBuilder {    private Dog dog;    public DogBuilder() {
this.dog = new Dog();
}
public static DogBuilder builder() {
return new DogBuilder();
}
public DogBuilder addName(String nome) {
this.dog.setName(nome);
return this;
}
public DogBuilder addBirthDate(LocalDate birthDate) {
this.dog.setBirthDate(birthDate);
return this;
}
public DogBuilder addBreed(String raca) {
this.dog.setRaca(raca);
return this;
}
public DogBuilder addWeight(Double peso) {
this.dog.setWeight(peso);
return this;
}
public DogBuilder addSex(String sexo) {
this.dog.setSex(sexo);
return this;
}
public Dog get() {
return this.dog;
}
}

After that, I protected the methods of the dog class, so that they are not misused:

import java.time.LocalDate;
import java.time.Period;
public class Dog {
private String name;
private LocalDate birthDate;
private String breed;
private String sex;
private Double weight;
protected Dog(){ } protected void setName(String name) {
this.name = name;
}
public void setBirthDate(LocalDate birthDate) {
this.birthDate = birthDate;
}
protected void setRaca(String raca) {
this.breed = raca;
}
protected void setSex(String sex) {
this.sex = sex;
}
public String getName() {
return name;
}
public Integer getAge() {
return Period.between(this.birthDate, LocalDate.now()).getYears();
}
public String getBreed() {
return breed;
}
public String getSex() {
return sex;
}
public Double getWeight() {
return weight;
}
public void setWeight(Double weight) {
this.weight = weight;
}
}

Note that only setWeight(…) is public. The rest is protected, that’s because it’s the only thing that can be updated over time.
Using a builder is like this:

import com.example.demo.english.builder.DogBuilder;
import com.example.demo.english.builder.Dog;
import java.time.LocalDate;public class BuilderTests { public static void main(String[] args) {
Dog dog = DogBuilder.builder()
.addName("Rex")
.addBirthDate(LocalDate.of(2015, 10, 10))
.addWeight(10.0)
.addSex("M")
.addBreed("Pitbull")
.get();
dog.getName();
}
}

Cool huh?

Now, just choose the form that makes the code more readable and go for it.

This code is here: https://github.com/mmarcosab/tres-formas-criar-objetos

As we say in Brazil: we are together.

--

--

Marcos
Javarevisited

I study software development and I love memes.