Builder Design Pattern with sub-classing and required parameters? - java

Recently I came into a situation where the builder pattern was very strong, but I had the need to subclass. I looked up some solutions and some suggested generics while others suggested normal subclassing. However, none of the examples I looked at had required fields in order to even begin building an object. I wrote a tiny example to illustrate where I'm getting stuck. At every turn I kept running into a wall of problems where things would return the wrong class, can't override static methods, returning super() returns the wrong data type, etc. I have a feeling there is no way out except excessive use of generics.
What is the correct way to go in this situation?
Tester
import person.Person;
import person.Student;
public class Tester
{
public static void main(String[] args)
{
Person p = Person.builder("Jake", 18).interest("Soccer").build();
// Student s = Student.builder(name, age) <-- It's weird that we still have access to pointless static method
// Student s = Student.builder("Johnny", 24, "Harvard", 3).address("199 Harvard Lane") <-- returns Person builder, not student
Student s = ((Student.Builder)Student.builder("Jack", 19, "NYU", 1).address("Dormitory")).build(); // really bad
}
}
Person Class
package person;
import java.util.ArrayList;
import java.util.List;
public class Person
{
// Required
protected String name;
protected int age;
// Optional
protected List<String> interests = new ArrayList<>();
protected String address = "";
protected Person(String name, int age)
{
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
public List<String> getInterests() { return interests; }
public String getAddress() { return address; }
// person.person does not allow builder construction
// unless all required fields are provided
/* Problem: I have to repeat the constructor fields here, very annoying */
public static Builder builder(String name, int age)
{
Person p = new Person(name, age);
return new Builder(p);
}
public static class Builder
{
Person reference;
protected Builder(Person reference)
{
this.reference = reference;
}
public Builder address(String address)
{
reference.address = address;
return this;
}
public Builder interest(String interest)
{
reference.interests.add(interest);
return this;
}
public Person build()
{
return reference;
}
}
}
Student Class
package person;
import java.util.ArrayList;
import java.util.List;
public class Student extends Person
{
// Required
protected String school;
protected int year;
// Optional
protected List<String> subjects = new ArrayList<>();
// This looks good
public Student(final String name, final int age, final String school, final int year)
{
super(name, age);
this.school = school;
this.year = year;
}
public String getSchool() { return school; }
public int getYear() { return year; }
public List<String> getSubjects() { return subjects; }
/* Here's where my issues are:
* Override doesn't compile on static methods but how else can I describe that I want to
* override this functionality from the Person class?
*
* Extending 'Person' does not enforce that I need to provide 'name', 'age', etc like it would
* if this was a normal design pattern using the 'new' keyword. I have to manually drag fields
* from 'person' and place them here. This would get VERY messy with an additional class
*
* User can STILL call the Person builder on a Student object, which makes no sense. */
/*#Override*/ public static Builder builder(String name, int age, String school, int year)
{
Student s = new Student(name, age, school, year);
return new Builder(s);
}
public static class Builder extends Person.Builder
{
// Student reference; <--- this should not be needed since we already
// have a variable for this purpose from 'Person.Builder'
public Builder(final Student reference)
{
super(reference);
}
/* Things begins to get very messy here */
public Builder subject(String subject)
{
((Student)reference).subjects.add(subject);
// I guess I could replace the reference with a student one, but
// I feel like that infringes on calling super() builder since we do the work twice.
return this;
}
#Override public Student build()
{
// I can either cast here or
// rewrite 'return reference' every time.
// Seems to infringe a bit on subclassing.
return (Student)super.build();
}
}
}

What you write here :
Student s = ((Student.Builder)Student.builder("Jack", 19, "NYU", 1).address("Dormitory")).build(); // really bad
is indeed not very natural and you should not need to cast.
We expect rather something like :
Student s = Student.builder("Jack", 19, "NYU", 1).address("Dormitory")).build();
Besides all casts you did in the implementation of Student.Builder are also noise and statements that may fail at runtime :
/* Things begins to get very messy here */
public Builder subject(String subject) {
((Student)reference).subjects.add(subject);
return this;
}
#Override public Student build() {
return (Student)super.build();
}
Your main issue is the coupling between the Builder classes and the building methods.
A important thing to consider is that at compile time, the method binding (method selected by the compiler) is performed according to the declared type of the target of the invocation and the declared type of its arguments.
The instantiated type is considered only at runtime as the dynamic binding is applied: invoking the method bounded at compile time on the runtime object.
So this overriding defined in Student.Builder is not enough :
#Override public Student build() {
return (Student)super.build();
}
As you invoke :
Student.builder("Jack", 19, "NYU", 1).address("Dormitory").build();
At compile time, address("Dormitory") returns a variable typed as Person.Builder as the method is defined in Person.Builder :
public Builder address(String address){
reference.address = address;
return this;
}
and it not overriden in Student.Builder.
And at compile time, invoking build() on a variable declared as Person.Builder returns a object with as declared type a Person as the method is declared in Person.Builder as :
public Person build(){
return reference;
}
Of course at runtime, the returned object will be a Student as
Student.builder("Jack", 19, "NYU", 1) creates under the hood a Student and not a Person.
To avoid cast to Student.builder both from the implementation and the client side, favor composition over inheritancy :
public static class Builder {
Person.Builder personBuilder;
private Student reference;
public Builder(final Student reference) {
this.reference = reference;
personBuilder = new Person.Builder(reference);
}
public Builder subject(String subject) {
reference.subjects.add(subject);
return this;
}
// delegation to Person.Builder but return Student.Builder
public Builder interest(String interest) {
personBuilder.interest(interest);
return this;
}
// delegation to Person.Builder but return Student.Builder
public Builder address(String address) {
personBuilder.address(address);
return this;
}
public Student build() {
return (Student) personBuilder.build();
}
}
You can now write :
Student s = Student.builder("Jack", 19, "NYU", 1)
.address("Dormitory")
.build();
or even that :
Student s2 = Student.builder("Jack", 19, "NYU", 1)
.interest("Dance")
.address("Dormitory")
.build();
Composition introduces generally more code as inheritancy but it makes the code
both more robust and adaptable.
As a side note, your actual issue is enough close to another question I answered 1 month ago.
The question and its answers may interest you.

A few thoughts as background
Static methods are not so great,
they make unit testing more difficult.
It is fine to put the builder as a static, nested class, but if you are using a builder to construct a class you should make the constructor not-public.
I prefer to have the builder be a separate class in the same package and to make the constructor (of the class that is created by the builder) package access.
Limit the builder constructor parameters.
I'm not a fan of using a class hierarchy for builders.
The Person and Student classes each have a builder.
Some Code
public class PersonBuilder
{
private String address;
private int age;
private final List<String> interestList;
private String name;
public PersonBuilder()
{
interestList = new LinkedList<>();
}
public void addInterest(
final String newValue)
{
// StringUtils is an apache utility.
if (StringUtils.isNotBlank(newValue))
{
interestList.add(newValue);
}
return this;
}
public Person build()
{
// perform validation here.
// check for required values: age and name.
// send all parameters in the constructor. it's not public, so that is fine.
return new Person(address, age, interestList, name);
}
public PersonBuilder setAddress(
final String newValue)
{
address = newValue;
return this;
}
public PersonBuilder setAge(
final int newValue)
{
age = newValue;
return this;
}
public PersonBuilder setInterestList(
final List<String> newValue)
{
interestList.clear();
if (CollectionUtils.isNotEmpty(newValue))
{
interestList.addAll(newValue);
}
return this;
}
public PersonBuilder setName(
final String newValue)
{
name = newValue;
return this;
}
}
public class Person
{
private Person()
{
}
Person(
final String addressValue,
final int ageValue,
final List<String> interestListValue,
final String name)
{
// set stuff.
// handle null for optional parameters.
}
// create gets or the fields, but do not create sets. Only the builder can set values in the class.
}

Related

Constructor Chaining with subclasses in Java

Just a question RE: Constructor Chaining in subclasses that I can't find a good answer on and I'm confusing myself a bit with.
I'm making a basic little Text Based RPG for some practice and I'm going through my constructors for an abstract class and have the constructors from 0-4 params chained together like below
abstract class Creature {
// Fields
private String name;
private int lifeForce;
private int strength;
private int agility;
// Constructors + Chaining
public Creature() {
this("Unknown")
}
public Creature(String name) {
this(name, 100);
}
public Creature(String name, int lifeForce) {
this(name, lifeForce, 10);
}
public Creature(String name, int lifeForce, int strength) {
this(name, lifeForce, strength, 10);
}
public Creature(String name, int lifeForce, int strength, int agility) {
this.name = name;
this.lifeForce = lifeForce;
this.strength = strength;
this.agility = agility;
}
My confusion is how best to format the constructors of a subclass of creature, for example this simple Person class introduces two new fields. There's definitely too much repetition if I write the constructors like this
// Constructors + Chaining
public Person() {
super("Unknown");
this.skillClass=new Mage();
this.dialogue="...";
}
public Person(String name) {
super(name);
this.skillClass=new Mage();
this.dialogue="...";
} etc etc etc
I suppose I could restrict the constructors to limit the repetition but I'm mostly just wondering if there's good best practice that I'm missing here.
Any and all suggestions welcome and if anyone has any good resources to recommend that go deeper than the usual
Class B extends Class A
examples I'd massively appreciate.
In situations like this one when you need to use multiple constructors with different parameters, it is recommended to use the builder pattern like this :
abstract class Creature {
// Fields
private String name;
private int lifeForce;
private int strength;
private int agility;
private Creature(Builder<?> builder) {
this.name = builder.name;
this.lifeForce = builder.lifeForce;
// Add the other attributes here.
}
public static abstract Builder extends Builder<T extends Builder<T>> {
private String name;
private int lifeForce;
private int strength;
private int agility;
public Builder(//here you put the attributes that you need to have in all instances) {
// here you do the affectations.
}
// now you need to make the functions that set each property :
public Builder lifeForce(int lifeForce) {
this.lifeForce = lifeForce;
return this;
}
// you do the same thing for all the other attributes.
...
public Creature build() {
return new Creature(this);
}
}
}
So for the explanation : This pattern will allow you to create instances of your class by setting only the needed attributes.
As here you have subclasses the builder pattern will be little bit more harder to understand but it is the perfect solution in such situation.
We need to apply the builder pattern also for every subclasse so let's do it for the person class :
public class Person extends Creature {
private int anotherField;
public Person(Builder builder) {
super(builder);
this.anotherField = anotherField;
}
public static Builder extends Creature.Builder<Builder> {
public Builder(//add the fieldHere if it is needed in all class instances) {
// if the field is not mandatory you can omit this constructor but you need to put the function below.
}
public Builder anotherField(int anotherField) {
this.anotherField = anotherField;
}
public Person build() {
return new Person(this);
}
}
Now let me show you how tricky is this solution :
1/ declare person with 2 fields :
Person p1 = Person.Builder().name("name").anotherField(0).build();
2/ declare another one with just one field
Person p2 = Person.Builder().agility(1000).build();
Remark : In these two examples, i supposed that your builders' constructors don't have parameters. If for example the name is mandatory field :
Person p3 = Person.Builder("name").anotherField(0).build();
I wish that you had the idea about using builder pattern.

How to test an abstract superclass in Java

I'm a student studying software development (1st year) and the teaching language we are using is Java. We have covered basics, and most of OOP, but I've been practicing making a Shop Administration System and I've come up against something I can't reckon with.
I'm trying to unit test two classes which are both abstract superclasses of several other classes I plan on implementing, as per the UML below
Person Superclass and Employee subclass - both abstract
I've read through a series of posts on here and I see a lot of people were recommending things like power mock and mockito for making mock objects. I'm probably trying to learn too much at once as it is but basically I landed on concrete "wrapper" private classes in the unit test class that i used to polymorphically create the Employee objects (technically EmployeeWrapper objects), then unit testing all the public methods through the wrapper class.
I'm vaugely familiar with the term "bad code smell" and this really stinks. Is there a standard way of testing abstract superclasses without using things like Mockito and Power Mock? Or do i just need to suck it up and use things like that?
This is the code for the classes (with all method bodies removed so you dont have to read through a load of unimportant details
import java.time.LocalDateTime;
import java.util.Hashtable;
import java.util.Iterator;
public abstract class Employee extends Person {
private double hourlyRate;
private double hoursPerWeek;
private LocalDateTime dateOfEmploymentStart;
private LocalDateTime dateOfEmploymentEnd;
private Hashtable<LocalDateTime, Integer> shifts;
private static final double MINIMUM_WAGE = 8.0;
/**
* Constructor for Employee for all fields except dateOfHire, which is set to {#code LocalDateTime.now()}
*
* #param name
* #param email
* #param phoneNumber
* #param hourlyRate
* #param weeklyHours
* #throws IllegalArgumentException if name if blank or null
*/
public Employee(String name, String email, String phoneNumber, double hourlyRate, double weeklyHours) throws IllegalArgumentException {
super(name, email, phoneNumber);
this.setHourlyRate(hourlyRate);
this.setWeeklyHours(weeklyHours);
this.setDateOfEmploymentStart(LocalDateTime.now());
this.shifts = new Hashtable<LocalDateTime, Integer>();
}
/**
* Constructor for Employee that sets name, email and phoneNumber to provided args; and sets hourly rate and weeklyHours to 0
*
* #param name
* #param email
* #param phoneNumber
* #throws IllegalArgumentException if name is blank or null
*/
public Employee(String name, String email, String phoneNumber) throws IllegalArgumentException {
this(name, email, phoneNumber, MINIMUM_WAGE, 0);
}
/**
* Constructor for Employee that sets only name
*
* #param name
* #throws IllegalArgumentException
*/
public Employee(String name) throws IllegalArgumentException {
this(name, null, null);
}
}
and the Unit test class (with all test cases bar one removed, and that one method body is left empty - again to stop clutter:
import static org.junit.jupiter.api.Assertions.*;
import java.time.LocalDateTime;
import java.util.Hashtable;
import java.util.Set;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
class EmployeeTest {
private class EmployeeWrapper extends Employee {
public EmployeeWrapper(String name, String email, String phoneNumber, double hourlyRate, double weeklyHours) throws IllegalArgumentException {
super(name, email, phoneNumber, hourlyRate, weeklyHours);
}
public EmployeeWrapper(String name, String email, String phoneNumber) throws IllegalArgumentException {
super(name, email, phoneNumber);
}
public EmployeeWrapper(String name) throws IllegalArgumentException {
super(name);
}
}
private String nameValid, emailValid, phoneNumberValid;
private String nameInvalid, emailInvalid, phoneNumberInvalid;
private double hourlyRateValid, hourlyRateInvalidLow;
private double weeklyHoursValid, weeklyHoursInvalid;
private final double DEFAULT_HOURLY_RATE = 8;
private final double DEFAULT_WEEKLY_HOURS = 0;
private final String DEFAULT_EMAIL = "no email provided";
private final String DEFAULT_PHONE_NUMBER = "no phone number provided";
private final double MINIMUM_WAGE = 8.0;
private Employee employee;
private Hashtable<LocalDateTime, Integer> shiftsValid, shiftsInvalidEmpty;
private LocalDateTime dateTimeValid, dateTimePast, dateTimeFuture;
#BeforeEach
void setUp() throws Exception {
// valid employee
nameValid = "testname";
phoneNumberValid = "123456789";
emailValid = "test#test.test.com";
hourlyRateValid = 10.50;
weeklyHoursValid = 7.5;
employee = new EmployeeWrapper(nameValid, emailValid, phoneNumberValid, hourlyRateValid, weeklyHoursValid);
// test data
nameInvalid = "";
emailInvalid = ".test#test.com";
phoneNumberInvalid = "";
hourlyRateInvalidLow = 5;
weeklyHoursInvalid = -10;
dateTimeValid = LocalDateTime.of(2015, 6, 15, 13, 30);
dateTimePast = LocalDateTime.MIN;
dateTimeFuture = LocalDateTime.MAX;
shiftsValid = new Hashtable<LocalDateTime, Integer>();
shiftsValid.put(dateTimeValid, 6);
shiftsValid.put(dateTimeFuture, 3);
shiftsInvalidEmpty = new Hashtable<LocalDateTime, Integer>();
}
#Test
void testEmployeeConstructorValidAllArgs() {
}
}
This is my first post of Stack Overflow so i apologise profusely if i have omitted any revelent details.
If you see any other stupid things i've done in the code I'll also gladly take any criticism!
edit: thanks everyone for the responses they have been amazing, i really really appreciate it!
First let me say, that your approach is absolutely viable. I am just sharing my own way of doing it, because it spares copy pasting tests common between different implementations.
I don't specifically test abstract classes. Because we are testing functionality and it can be overridden in subclasses. I'll use your Person class for this setup, but i will simplify it a bit.
public abstract class Person {
private String name;
private String email;
public Person(String name, String email) {
this.setName(name);
this.email = email;
}
public String getName() {
return this.name;
}
public void setName(String name) {
if (name == null || name.isEmpty()) {
throw new IllegalArgumentException("missing name");
}
this.name = name;
}
public String getEmail() {
return this.email;
}
public void setEmail(String email) {
this.email = email;
}
}
Student
public class Student extends Person {
private String university;
public Student(String name, String email, String university) {
super(name, email);
this.university = university;
}
public String getUniversity() {
return this.university;
}
public void setUniversity(String university) {
this.university = university;
}
}
Child
public class Child extends Person {
private String school;
public Child(String name, String email, String school) {
super(name, email);
this.school = school;
}
public String getSchool() {
return this.school;
}
public void setSchool(String school) {
this.school = school;
}
#Override
public String getName() {
return "I am not saying!";
}
}
So we have the abstract Person, a Student, whose specific thing is a university and a Child. Having a school is what is specific for the child, but it also changes the behaviour of getName(), it does not disclose its name. This might be desired, but for this example we'll assume it was incorrect to override getName() like this.
When dealing with abstract classes i make an abstract test class, which holds common setup and tests for common functionality provided by the abstract class - Person in this case.
public abstract class PersonBaseTests {
protected static final String EXPECTED_NAME = "George";
private Person person;
#BeforeEach
public void setUp() {
this.person = getConcretePersonImplementation();
}
/**
* #return new instance of non-abstract class extending person
*/
protected abstract Person getConcretePersonImplementation();
//common tests
#Test
public void testGetName_ShouldReturnCorrectValue() {
assertEquals(EXPECTED_NAME, this.person.getName());
}
#Test
public void testConstructor_ShouldThrowIllegalArgumentExceptionOnMissingName() {
Executable invalidConstructorInvocation = getConstructorExecutableWithMissingName();
IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, invalidConstructorInvocation);
assertEquals("missing name", exception.getMessage());
}
protected abstract Executable getConstructorExecutableWithMissingName();
//other common tests
}
The test classes extending the base must provide the concrete implementation to be tested. They will also inherit the tests, so you don't need to write them again. If you still have not learned about interfaces, lambdas and stuff like that, you can ignore the constructor exception test and everything related to it, and focus on getName() test. It tests that the getter correctly returns the name of the Person. This will obviously fail for Child, but that's the idea. You can add tests for getting and setting email, phone, etc.
So, student tests
public class StudentTests extends PersonBaseTests {
#Override
protected Person getConcretePersonImplementation() {
return new Student(PersonBaseTests.EXPECTED_NAME, "mail", "Cambridge");
}
#Override
protected Executable getConstructorExecutableWithMissingName() {
//setup invocation which will actually fail
return new StudentConstructorExecutable(null, "email#email.email", "Stanford");
}
private static final class StudentConstructorExecutable implements Executable {
private final String name;
private final String email;
private final String university;
private StudentConstructorExecutable(String name, String email, String university) {
this.name = name;
this.email = email;
this.university = university;
}
#Override
public void execute() throws Throwable {
//this will invoke the constructor with values from fields
new Student(this.name, this.email, this.university);
}
}
//write tests specific for student class
//getUniversity() tests for example
}
Again, ignore the Executable and everything related to the constructor test, if you have not learned it yet. Student tests provide concreete instance of Student for the common inherited tests, and you can write additional tests for specific functionality - get/set university.
Child tests
public class ChildTests extends PersonBaseTests {
#Override
protected Person getConcretePersonImplementation() {
return new Child(PersonBaseTests.EXPECTED_NAME, "", "some school");
}
#Override
protected Executable getConstructorExecutableWithMissingName() {
//this can be ignored
return () -> new Child(null, "", "");
}
//write tests specific for child class
//getSchool() tests for example
}
Again, a concrete instance is provided for the common tests - this time of type Child. And you can add tests for any additional functionality provided by Child class - get and set school in this example. Then you can write more test classes for every additional subclass of Person.
Like this you keep common tests at one place and every concrete implementation of abstract class you write is completely tested, without test duplication. About the failing test, if the change in behaviour of getName() is intentional, you can just override it in ChildTests to take that into account. If it is not intentional, you know, that Student.getName() is correct, while Child.getName() is not, but you wrote the test only once.
as you stated you can't test abstract classes in Java. You need a mocking framework like Mockito or a concrete class that extends your superclass, in this case Employee.
And that's what you have done with your class EmployeeWrapper. Except for the name (i would name it EmployeeImpl) i'm fine with your solution.

Builder pattern vs encapsulation of a data

How should I retain Encapsulation Principle of OOP, when using builder pattern? I mean fact that builder should provide abstraction layer between object and the code that uses it, so that it can be constructed part-by-part, requires either making setter for every parameter of an object that we would normally pass in the constructor. That again may be undesirable in some cases, as I don't want client to be able to modify value that I have to via builder. Example to picture what I mean is below:
public class Cat
{
private string _race;
private string _name;
public Cat()
{
_race = "common";
_name = string.Empty;
}
public void setRace(string race) { _race = race; }
public void setName(string name) { _name = name; }
}
public class CatBuilder
{
private Cat _objectUnderConstruction;
public CatBuilder() { _objectUnderConstruction = new Cat(); }
public CatBuilder WithName(string name)
{
_objectUnderConstruction.setName(name);
return this;
}
public CatBuilder OfRace(string race)
{
_objectUnderConstruction.setRace(race);
return this;
}
}
This is not production code, I wrote it now with presentation in mind, so do not get mad on how it is constructed.
In the example above there is need to set cat's race, as we need that information for the purpose of object filling, so we need to pass info into it. At the same time I don't want anyone to ever change race of my cat during its lifetime (e.g. it would change from egyptian to british in the middle of processing) Normally I would get rid of accessor method, but I need for the builder. This way, encapsulation of data is hurt (because straight get and set aren't encapsulating anything), and I want to avoid it.
This example is simple and I could pass parameter in constructor, but imagine bigger class, where there is a lot of such fields, what in this case? Should I pass some configuration object inside (which is almost like builder, but simpler, hence builder is pointless) or pass the builder itself to the constructor (which is weird, but what do I know)?
How I should do that?
If your builder is tightly-coupled with your class you can make Builder subclass of the object being constructed:
public class Cat
{
private string _race;
private string _name;
public Cat()
{
_race = "common";
_name = string.Empty;
}
private void setRace(string race) { _race = race; }
private void setName(string name) { _name = name; }
public class Builder
{
private Cat _objectUnderConstruction;
public CatBuilder() { _objectUnderConstruction = new Cat(); }
public CatBuilder WithName(string name)
{
_objectUnderConstruction.setName(name);
return this;
}
public CatBuilder OfRace(string race)
{
_objectUnderConstruction.setRace(race);
return this;
}
}
}
This way, you'll be able in Builder to access private fields and methods of Cat and use it like new Cat.Builder().OfRace("").OfName("").Build().

Testing builder pattern in Java

We use the builder pattern extensively in our code base, with built objects all having a toBuilder() method. I want to write a unit test that ensures that no fields have been forgotten in the toBuilder() methods, i.e., for any buildable object, I want to a test roughly like this
MyClass obj = getTestObjectWithRandomData();
assertEquals(obj, obj.toBuilder().build());
Now, I can fairly easy write a basic version of getTestObjectWithRandomData() that uses reflection to assign a bunch of values to the fields of any object. However, the snag is that build() often contains tons of validation checks that will throw exceptions if, for example, a certain integer isn't within a sane range. Writing a generalized version of getTestObjectWithRandomData() that conforms all those class-specific validation checks would be impossible.
So, how can I do what I want to do? I'm tempted to segregate the construction and validation code into different methods so that the test doesn't trip on the validation, but then that means that people have to remember to call validate() or whatever on objects after they create them. Not good.
Any other ideas?
How about using Lombok? Would that be an option for you? It will auto-generate the builder code and you'll never again have to worry about it.
https://projectlombok.org/features/Builder
Simply annotate your classes with #Builder
With Lombok
import lombok.Builder;
import lombok.Singular;
import java.util.Set;
#Builder
public class BuilderExample {
private String name;
private int age;
#Singular private Set<String> occupations;
}
Vanilla Java
import java.util.Set;
public class BuilderExample {
private String name;
private int age;
private Set<String> occupations;
BuilderExample(String name, int age, Set<String> occupations) {
this.name = name;
this.age = age;
this.occupations = occupations;
}
public static BuilderExampleBuilder builder() {
return new BuilderExampleBuilder();
}
public static class BuilderExampleBuilder {
private String name;
private int age;
private java.util.ArrayList<String> occupations;
BuilderExampleBuilder() {
}
public BuilderExampleBuilder name(String name) {
this.name = name;
return this;
}
public BuilderExampleBuilder age(int age) {
this.age = age;
return this;
}
public BuilderExampleBuilder occupation(String occupation) {
if (this.occupations == null) {
this.occupations = new java.util.ArrayList<String>();
}
this.occupations.add(occupation);
return this;
}
public BuilderExampleBuilder occupations(Collection<? extends String> occupations) {
if (this.occupations == null) {
this.occupations = new java.util.ArrayList<String>();
}
this.occupations.addAll(occupations);
return this;
}
public BuilderExampleBuilder clearOccupations() {
if (this.occupations != null) {
this.occupations.clear();
}
return this;
}
public BuilderExample build() {
// complicated switch statement to produce a compact properly sized immutable set omitted.
// go to https://projectlombok.org/features/Singular-snippet.html to see it.
Set<String> occupations = ...;
return new BuilderExample(name, age, occupations);
}
#java.lang.Override
public String toString() {
return "BuilderExample.BuilderExampleBuilder(name = " + this.name + ", age = " + this.age + ", occupations = " + this.occupations + ")";
}
}
}

Good way to create a immutable class with modifiers (thread-safe)

I have a case when I want to avoid defensive copies, for data which might nevertheless be modified, but is usually simply read, and not written to. So, I'd like to use immutable objects, with functional mutator methods, which is kind of usual (java lombok is able to do it more or less automatically). The way I proceed is the following:
public class Person {
private String name, surname;
public Person(String name, String surname) {....}
// getters...
// and instead of setters
public Person withName(String name) {
Person p= copy(); // create a copy of this...
p.name= name;
return p;
}
public Person copy() {....}
}
So, to get a copy of the person with a different name, I would call
p= new Person("Bar", "Alfred");
...
p= p.withName("Foo");
In practice, the objects are rather large (and I ended up using serialization to avoid the burden of writing the copy code).
Now, while browsing the web, I see a potential concurrency problem with this implementation, as my fields are not final, and thus, concurrent access might see the returned copy, for instance, without the new name change (as there is no warrantee on the order of operation in this context).
Of course, I can't make my fields final, with the current implementation, as I first do a copy, and then change the data in the copy.
So, I'm looking for a good solution for this problem.
I might use volatile, but I feel it's not a good solution.
Another solution would be to use the builder pattern:
class PersonBuilder {
String name, surname; ....
}
public class Person {
private final String name, surname;
public Person(PersonBuilder builder) {...}
private PersonBuilder getBuilder() {
return new PersonBuilder(name, surname);
}
public Person withName(String name) {
PersonBuilder b= getBuilder();
b.setName(name);
return new Person(b);
}
}
Is there any problem here, and above all, is there a more elegant way of doing the same thing ?
I recommend you take a look at Guava's immutable collections, such as immutable list and how they create lists from builders etc.
The idiom is the following:
List<String> list1 = ImmutableList.of("a","b","c"); // factory method
List<String> list2 = ImmutableList.builder() // builder pattern
.add("a")
.add("b")
.add("c")
.build();
List<String> list3 = ... // created by other means
List<String> immutableList3 = ImmutableList.copyOf(list3); // immutable copy, lazy if already immutable
I really like the idiom above. For an entity builder I would take the following approach:
Person johnWayne = Person.builder()
.firstName("John")
.lastName("Wayne")
.dob("05-26-1907")
.build();
Person johnWayneClone = johnWayne.copy() // returns a builder!
.dob("06-25-2014")
.build();
The builder here can be obtained from an existing instance via the copy() method or via a static method on the Person class (a private constructor is recommended) that return a person builder.
Note that the above mimics a little Scala's case classes in that you can create a copy from an existing instance.
Finally, don't forget to follow the guidelines for immutable classes:
make the class final or make all getters final (if the class can be extended);
make all fields final and private;
initialize all fields in the constructor (which can be private if you provide a builder and/or factory methods);
make defensive copies from getters if returning mutable objects (mutable collections, dates, third party classes, etc.).
One possibility is to separate your interfaces surrounding such objects into an immutable variant (providing getters) and a mutable variant (providing getters and setters).
public interface Person {
String getName();
}
public interface MutablePerson extends Person {
void setName(String name);
}
It doesn't solve the mutability of the object per se but it does offer some guarantees that when you pass around the object using the immutable interface reference, you know that the code you're passing this to won't change your object. Obviously you need to control the references to the underlying object and determine the subset of functionality that has control of a reference via the mutable interface.
It doesn't solve the underlying problem and I would favour immutable objects until I definitely need a mutable version. The builder approach works nicely, and you can integrate it within the object to give a modifier thus:
Person newPerson = existingPerson.withAge(30);
Why not make your fields final and your modifier methods directly create new objects?
public class Person {
private final String name, surname;
public Person(String name, String surname) {....}
// getters...
// and instead of setters
public Person withName(String newName) {
return new Person(newName, surname);
}
}
Your problem boils down to this: You want a method that safely publishes an effectively immutable, almost-but-not-quite-faithful copy of an effectively immutable object.
I'd go with the builder solution: It's verbose as all get out, but Eclipse helps with that, and it allows all of the published objects to be actually immutable. Actual immutability makes safe publication a no-brainer.
If I wrote it, it'd look like this:
class Person {
public static final FooType DEFAULT_FOO = ...;
public static final BarType DEFAULT_BAR = ...;
public static final BazType DEFAULT_BAZ = ...;
...
private final FooType foo;
private final BarType bar;
private final BazType baz;
...
private Person(Builder builder) {
this.foo = builder.foo;
this.bar = builder.bar;
this.baz = builder.baz;
...
}
public FooType getFoo() { return foo; }
public BarType getBar() { return bar; }
public BazType getBaz() { return baz; }
...
public Person cloneWith(FooType foo) {
return new Builder(this).setFoo(foo).build();
}
public Person cloneWith(BarType bar) {
return new Builder(this).setBar(bar).build();
}
public Person cloneWith(FooType foo, BarType bar) {
return new Builder(this).setFoo(foo).setBar(bar).build();
}
...
public class Builder{
private FooType foo;
private BarType bar;
private BazType baz;
...
public Builder() {
foo = DEFAULT_FOO;
bar = DEFAULT_BAR;
baz = DEFAULT_BAZ;
...
}
public Builder(Person person) {
foo = person.foo;
bar = person.bar;
baz = person.baz;
...
}
public Builder setFoo(FooType foo) {
this.foo = foo;
return this;
}
public Builder setBar(BarType bar) {
this.bar = bar;
return this;
}
public Builder setBaz(BazType baz) {
this.baz = baz;
return this;
}
...
public Person build() {
return new Person(this);
}
}
}
Depends on how many fields you intend to change. You could make special Changed objects like:
interface Person {
public String getForeName();
public String getSurName();
}
class RealPerson implements Person {
private final String foreName;
private final String surName;
public RealPerson (String foreName, String surName) {
this.foreName = foreName;
this.surName = surName;
}
#Override
public String getForeName() {
return foreName;
}
#Override
public String getSurName() {
return surName;
}
public Person setSurName (String surName) {
return new PersonWithSurnameChanged(this, surName);
}
}
class PersonWithSurnameChanged implements Person {
final Person original;
final String surName;
public PersonWithSurnameChanged (Person original, String surName) {
this.original = original;
this.surName = surName;
}
#Override
public String getForeName() {
return original.getForeName();
}
#Override
public String getSurName() {
return surName;
}
}
This may also mitigate the problem you have with cloning heavy objects.

Categories

Resources