State

From cdd
Jump to: navigation, search

The decision to separate classes into state, behavior and creator, allow us to focus more on the state creation process. Instead of creating an object in one step by using one ore more constructors, the state is first created, followed by encapsulation which gives a number of advantages:

  • The behaviour representation need only one constructor.
  • The state can be defined in a declarative way so that hashCode, equals and toString can be generated, letting us focus on the properties of each attribute.
  • The chained creator and/or builder pattern is used to build state which improves the usability compared to a set of constructors (or even worse, an empty constructor with a bunch of setters!)
  • The state can be explicitly validated before encapsulation takes place.
  • An InvalidXStateException (where X is the name of the state) is thrown if the state is invalid in the encapsulation step.
  • The encapsulation step can be postponed so that validation and encapsulation can be performed by the caller.
  • Support for switching between immutable, mutable, map and string state representations.


Let's create a state:

public class PersonState {
    int ssn;
    String name;
}

Regenerate and the code will look something like this (some code has been replaced with ...):

...
 
@State
public class PersonState implements ImmutableState {
    public final int ssn;
    public final String name;
 
    // ===== Generated code =====
 
    public static final String SSN = "ssn";
    public static final String NAME = "name";
 
    public PersonState(
            int ssn,
            String name) {
        this.ssn = ssn;
        this.name = name;
 
        if (name == null) throw new InvalidPersonStateException("'name' can not be null");
    }
 
    public static class InvalidPersonStateException extends InvalidStateException { ... }
 
    // getters and setters
 
    public PersonMutableState asMutable() { ... }
    public PersonStringState asStringState() { ... }
    public PersonStringState asStringState(StringStateConverter converter) { ... }
    public PersonStringState asStringState(PersonStringStateConverter converter) { ... }
 
    public int hashCode() { ... }
    public boolean equals(Object that) { ... }
    public String toString() { ... }
 
    @State(type = "mutable")
    public static class PersonMutableState implements MutableState {
        public int ssn;
        public String name;
 
        // constructors
        // getters and setters
 
        public PersonState asImmutable() { ... }
        public Map asMap() { ... }
        public PersonStringState asStringState() { ... }
        public PersonStringState asStringState(StringStateConverter converter) { ... }
        public PersonStringState asStringState(PersonStringStateConverter converter) { ... }
 
        public boolean isValid() { ... }
        public ValidationErrors validate() { ... }
        public void assertIsValid() { ... }
 
        // hashCode/equals/toString
    }
 
    @State(type = "string")
    public static class PersonStringState implements StringState {
        public String ssn;
        public String name;
 
        // constructors
        // getters and setters
 
        public PersonState asImmutable() { ... }
        public PersonState asImmutable(PersonStringStateConverter converter) { ... }
        public PersonMutableState asMutable() { ... }
        public PersonMutableState asMutable(PersonStringStateConverter converter) { ... }
        public Map asMap() { ... }
 
        public boolean isValid() { ... }
        public ValidationErrors validate() { ... }
        public ValidationErrors validate(PersonStringStateValidator stateValidator) { ... }
        public void assertIsValid() { ... }
        public void assertIsValid(PersonStringStateValidator stateValidator) { ... }
 
        // hashCode/equals/toString
    }
}


The immutable state PersonState is complemented with support for three more state representations, ending up with these representations:

  1. PersonState
  2. PersonMutableState.
  3. PersonStringState.
  4. java.util.Map


It is possible to move between these four state representations just by calling the corresponding as-method, but more on that later!


CDD ensures that every attribute is public final and that all types are immutable. If you for example defines your (immutable) state like:

public class PersonState {
    int ssn;
    String name;
    Map<String,AddressState> map;
}


...CDD will help you make all attributes "public final" and immutable. After generation the state will look like this:

public class PersonState {
    public final int ssn;
    public final String name;
    public final ImmutableMap<String,AddressState> map;
}


...but the class PersonState.PersonMutableState will of course be mutable:

public class PersonMutableState {
    int ssn;
    String name;
    Map<String,AddressMutableState> map;
 
    ...
}


< Back          Next >