Avoiding constructors with parameters of same type
This tiny tip shows how to prevent problems with the usage of your constructors.
Please take a look at the following negative example first:
package demo; import java.util.Arrays; import java.util.List; public class Tip0011 { private static class Person { private String firstname; private String middlename; private String surname; public Person(String firstname, String middlename, String surname) { this.firstname = firstname; this.surname = surname; } public String getFirstname() { return firstname; } public void setFirstname(String firstname) { this.firstname = firstname; } public String getMiddlename() { return middlename; } public void setMiddlename(String middlename) { this.middlename = middlename; } public String getSurname() { return surname; } public void setSurname(String surname) { this.surname = surname; } } public static void main(String[] args) { List<Person> persons = Arrays.asList( new Person("Robert", "C.", "Martin"), new Person("John", "R. R.", "Tolkien")); persons.forEach(person -> System.out.println(person.getSurname() + ", " + person.getFirstname() + " " + person.getMiddlename())); } }
How easily can a 'Person' be created in the following way:
Person person = new Person("Washington", null, "George");
The same problem often occurs with tabular data as well:
private class TableData { public TableData(int nColumns, int nRows) { … } public reset(int nRows, int nColumns) { … } }
At least the constructors' parameters where named well.
One possible solution would be, to omit the constructor:
package demo; import java.util.Arrays; import java.util.List; public class Tip0011b { private static class Person { private String firstname; private String middlename; private String surname; public String getFirstname() { return firstname; } public void setFirstname(String firstname) { this.firstname = firstname; } public String getMiddlename() { return middlename; } public void setMiddlename(String middlename) { this.middlename = middlename; } public String getSurname() { return surname; } public void setSurname(String surname) { this.surname = surname; } } public static void main(String[] args) { Person uncleBob = new Person(); uncleBob.setFirstname("Robert"); uncleBob.setMiddlename("C."); uncleBob.setSurname("Martin"); Person lotr = new Person(); lotr.setFirstname("John"); lotr.setMiddlename("R. R."); lotr.setSurname("Tolkien"); List<Person> persons = Arrays.asList( uncleBob, lotr); persons.forEach(person -> System.out.println(person.getSurname() + ", " + person.getFirstname() + " " + person.getMiddlename())); } }
It's clear that the source code is now getting longer and more difficult to read.
From time to time one can find solutions wtith Wrapper -Classes like
public class Surname {} public class Firstname {} public class Middlename {} public class TableDimension { public int nColumns; public int nRows; }
The class TableDimension may not have a constructor with two parameters. Otherwise, the problem would just be postponed.
Overall we will get a lot of classes.
We suggest that you should use the Builder design pattern:
package demo.person; import java.util.Arrays; import java.util.List; public class Person { private String firstname; private String middlename; private String surname; public String getFirstname() { return firstname; } void setFirstname(String firstname) { this.firstname = firstname; } public String getMiddlename() { return middlename; } void setMiddlename(String middlename) { this.middlename = middlename; } public String getSurname() { return surname; } void setSurname(String surname) { this.surname = surname; } }
package demo.person; public class PersonBuilder { private Person person = new Person(); public PersonBuilder firstname(String name) { person.setFirstname(name); return this; } public PersonBuilder surname(String name) { person.setSurname(name); return this; } public PersonBuilder middlename(String name) { person.setMiddlename(name); return this; } public Person build() { return person; } }
package demo; public class Tip0011c { public static void main(String[] args) { List<Person> persons = Arrays.asList( new PersonBuilder().firstname("Robert") .middlename("C.") .surname("Martin") .build(), new PersonBuilder().firstname("John") .middlename("R. R.") .surname("Tolkien") .build()); persons.forEach(person -> System.out.println(person.getSurname() + ", " + person.getFirstname() + " " + person.getMiddlename())); } }
Please note, that we introduced package demo.person and declared the Setter s package-private .
You can find implementations in which the class gets Person Setter-like methods, which return the Person -Object (this). In this case the class has two responsibilities: data transportation and building. That is ok, but it violates the Single-Responsibilty principle. See also S OLID .