State builder

From cdd
Jump to: navigation, search

So far we have used chained creators to build state. In situations when you don't have control of in which order the attributes are going to be set, a builder can be more convenient.


Let's start with an example (make sure you have made the static import of method PersonCreator.buildPerson):

buildPerson().asPerson();

output:

Exception in thread "main" com.myproject.PersonState$InvalidPersonStateException: [ValidationError{attribute='ssn', errorType='NULL', errorMessage='Attribute 'ssn' can not be NULL'}, ValidationError{attribute='givenName', errorType='NULL', errorMessage='Attribute 'givenName' can not be NULL'}]
	at com.myproject.PersonState$PersonMutableState.assertIsValid(PersonState.java:244)
	at com.myproject.PersonState$PersonMutableState.asImmutable(PersonState.java:184)
	at com.myproject.PersonCreator$PersonBuilder.asState(PersonCreator.java:266)
	at com.myproject.Main.main(Main.java:9)
    ...

The state could not be encapsulated because the mandatory attributes ssn and givenName was not set. A chained creator helps you to set all mandatory attributes, but that is not the case with builders and is the reason why chained creators is the preferable choice in many situations.


The next example shows how a builder can be used:

PersonBuilder builder = buildPerson();
 
builder.withSsn(123); // Set one attribute at a time...
builder.withGivenName("Ingmar").withSurname("Bergman"); // ...or use method chaining.
 
System.out.println(builder.asState());


output:

{ssn=123, givenName="Ingmar", surname="Bergman"}


Build object hierarchies

Builders can also be used to create more complex objects. Create these four classes:

public class AddressState {
    String streetName;
    String city;
}
 
public class PersonState implements ImmutableState {
    int ssn;
    String name;
    AddressState address;
}
 
public class AddressCreator {
    AddressState state;
 
    Address asAddress() {
        return new Address(state.asImmutable());
    }
}
 
public class PersonCreator {
    PersonState state;
 
    public Person asPerson() {
        return new Person(state.asImmutable());
    }
}


...regenerate and create these two:

import static com.myproject.AddressCreator.AddressBehaviour;
 
public class Address extends AddressBehaviour {
    public Address(AddressState state) {
        super(state);
    }
}
 
import static com.myproject.PersonCreator.PersonBehaviour;
 
public class Person extends PersonBehaviour {
    public Person(PersonState state) {
        super(state);
    }
}


...create and execute:

import static com.myproject.AddressCreator.buildAddress;
import static com.myproject.PersonCreator.buildPerson;
 
public class Main {
 
    public static void main(String[] args) {
        Person person = buildPerson().withSsn(123).withName("Carl").withAddress(
                buildAddress().withStreetName("Regent street").withCity("London")
        ).asPerson();
 
        System.out.println(person);
    }
}


Note that you have to write buildAddress and not createAddress in this case.


output:

Person{ssn=123, name="Carl", address={streetName="Regent street", city="London"}}


< Back          Next >