The different possible usages are described with a simple example Person. To compare with a 'type less' implementation, also a implementation with simple String is shown.
This Person example is very simple. The Person consists of first name and surname. Without value object your person would look like this
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | public class Person { private final String firstname; private final String surname; public Person(String firstname, String surname) { this .firstname = firstname; this .surname = surname; } public String getFirstname() { return firstname; } public String getSurname() { return surname; } } |
You will use this class as usual
25 | Person person = new Person( "firstname" , "surname" ); |
Note that the parameters can be swapped without errors, you will have to write a test spot this error.
Now with value object, you first have to write your value objects.
21 22 23 24 25 26 27 | public class Firstname extends StringWrapper<Firstname> { public Firstname(String value) { super (value); } } |
21 22 23 24 25 26 27 | public class Surname extends StringWrapper<Surname> { public Surname(String value) { super (value); } } |
And with these types your Person would look like this
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | public class Person { private final Firstname firstname; private final Surname surname; public Person(Firstname firstname, Surname surname) { this .firstname = firstname; this .surname = surname; } public Firstname getFirstname() { return firstname; } public Surname getSurename() { return surname; } } |
The usage of this class also simple
25 | Person person = new Person( new Firstname( "firstname" ), new Surname( "surname" )); |
But here you can not swap the names, you will get a compiler error.
When you let generate the implementation classes, you have to write interfaces for the types.
21 22 23 | public interface Firstname extends Value<Firstname, String> { } |
21 22 23 | public interface Surname extends Value<Surname, String> { } |
Here you create the instances of your types with the TypeString factory class. The naming schema for the factory classes is Type<TechnicalType>.
29 30 31 | Firstname first = TypeString.create(Firstname. class , "first" ); Surname surname = TypeString.create(Surname. class , "surname" ); Person customer = new Person(first, surname); |
The change looks very small, but has a great impact. You have to imagine that the type was changed from String to e.g. FirstName. Accidentally confuse first name and surname is not possible. The compiler will report an error in such cases.