Reputation: 337
For fundamental types the builder pattern seems to be pretty straight forward. I wanted to understand how it works when you want to build an object having complex member types.
Here I have a Person class and a static inner class to build Person. I have an Address Class that I have defined in the PersonBuilder static nested class. Ideally the Address Class should have been defined in the Person class itself. The code works but I am not sure If I am doing things correctly here. Can anyone please advise if there is a better way to construct these kind of objects.
class Person{
private String firstName;
private String lastName;
private Integer age;
private Person.PersonBuilder.Address address;
private Person(PersonBuilder builder){
this.firstName = builder.firstName;
this.lastName = builder.lastName;
this.age = builder.age;
this.address = builder.address;
}
@Override
public String toString(){
return "Person: " +
this.firstName + "|" +
this.lastName + "|" +
this.age.toString() + "|" +
this.address.aptNum + "|" +
this.address.street + "|" +
this.address.city + "|" +
this.address.state + "|" +
this.address.zipCode;
}
public static class PersonBuilder{
private String firstName;
private String lastName;
private Integer age;
private Address address;
private class Address{
private String aptNum;
private String street;
private String city;
private String state;
private Long zipCode;
public Address(String aptNum, String street, String city, String state, Long zipCode) {
this.aptNum = aptNum;
this.street = street;
this.city = city;
this.state = state;
this.zipCode = zipCode;
}
}
public PersonBuilder(String firstName, String lastName){
this.firstName = firstName;
this.lastName = lastName;
this.address = this.new Address("", "", "", "", 0L);
}
public PersonBuilder age(Integer age){
this.age = age;
return this;
}
public PersonBuilder buildAddress(String aptNum, String street, String city, String state, Long zipCode){
this.address = new Address(aptNum, street, city, state, zipCode);
return this;
}
public Person build(){
return new Person(this);
}
}
}
class Demo {
public static void main(String[] args){
Person p1 = new Person.PersonBuilder("XYZ", "XYZ")
.age(24)
.buildAddress("AB", "XYZ Lane", "ABCtown",
"XY", 1234L)
.build();
System.out.println(p1.toString());
}
}
Upvotes: 2
Views: 2756
Reputation: 10395
I think it would be better to define Address
as public class separately, so it will not be tightly coupled with Person
and you can reuse it.
You can also create a builder for Address
. Even if all fields are required, it will make address initialization easier to read.
It will look like this:
Address a1 = new Address.AddressBuilder()
.aptNum("AB")
.street("XYZ Lane")
.city("ABCtown")
.state("XY")
.zipCode("1234L")
.build();
Person p1 = new Person.PersonBuilder("XYZ", "XYZ")
.age(24)
.address(a1)
.build();
Upvotes: 1
Reputation: 419
A class should have a single main purpose, but it seems that you are giving two different tasks to PersonBuilder
: building Persons and constructing Addresses (which is done on the fly while building Person instances).
As suggested before, it would be better if you could make Address
an independent class and add an AddressBuilder
there.
This will make it easier for you to modify Person
and Address
classes independently.
The address field setter in PersonBuilder
will also look cleaner, as it could simply receive an already built Address
instance:
public PersonBuilder address(Address address){
this.address = address;
return this;
}
Usage would be similar to the example mentioned in a previous answer:
Address address = new Adress.AdressBuilder()
.aptNum("xyz")
.street("xyz")
.city("xyz")
.state("xyz")
.zipCode("xyz")
.build();
Person person = new Person.PersonBuilder("ABCD", "EFG")
.address(address)
.build();
Upvotes: 2