In this post I want to discuss the subject of Value Objects, their purpose and some ways of easily implementing them in Java, specifically, although not exclusively, within the context of Android development.
I’ll cover a couple of popular libraries that I’ve tried, namely Project Lombok and AutoValue, how they approach the problem of making value objects easier to create and maintain, plus a few pitfalls.
In computer science, a value object is a small object that represents a simple entity whose equality is not based on identity: i.e. two value objects are equal when they have the same value, not necessarily being the same object. Examples of value objects are objects representing an amount of money or a date range.
When is it a Value Object and not an…?
Entity? There are many definitions and opinions, but the most common explanation is something like this…
A value object does not have identity. That is, we compare them to another object purely on its stored values, and if those match, they are treated as the same. Value objects do not have an “ID” per se*, they do not have a lifespan, you can create and destroy value objects as you wish, as long as they are structurally the same, they are equal.
*Here we veer into some of the more opinionated discussion. In the case where you are persisting value objects in say a database or cache, then you may need an ID for that, but the ID is not inherently part of what defines that object and is disposable outside of said persistence layer.
Why Value Objects?
Martin Fowler recently wrote a post on value objects which provides a nice easy to follow background on what they are, and something on (im)mutability. So what are some of the benefits of immutability of objects?
- Being immutable you can check they are valid on creation and they will stay that way.
- Help to avoid concurrency issues, if you can’t change the values of an object at runtime, you should not experience side effects down to timing/concurrent access (e.g. you start a timer, then call timer.setStartTime() after it has started, perhaps another thread attempts to access the timer’s values and things get wonky).
- As a developer hint – makes it clear these objects are simple containers for state at a given time (e.g. what a REST API call returned).
- In the pursuit of pure functions – pass an object as a parameter without fear of side effects.
- Examples of value objects? Addresses, API responses (e.g. using GSON), configurations.
What they are not good for:
- Objects that have an identity. Do you need to maintain that this is the same object all along whilst its state is changed? Perhaps a
User
? Can you simply replace it with an exact copy without experiencing a problem? - Performance – if you are creating lots of copies of objects by modifying values you may hit performance issues. Applications that edit documents/content/graphics may need special consideration or optimisations./
OK so value objects have a purpose, how do we go about making our regular classes into value object classes? First, let’s talk immutability.
Immutable?
At the core of value classes we have this concept of immutability; that is the inability to modify an object’s state/values, so let’s start with that.
To allow for this in Java we must:
- Remove any setters, assignment occurs at time of construction.
- Make use of
final
to prevent mutability of values that getters provide access to in subclasses. - Utilise factory methods to construct a ready to go value object.
- Use the Builder pattern to make the above more flexible and fluent.
Let’s look at an example…
class Circle {
private final float x;
private final float y;
private final float radius;
Circle(float x, float y, float radius) {
this.x = x;
this.y = y;
if(radius<0) {
throw new IllegalArgumentException("radius must be >= 0");
}
this.radius = radius;
}
public float getX() { return this.x; }
public float getY() { return this.y; }
public float getRadius() { return this.radius; }
@Override
public boolean equals(Circle c) {
return this.x==c.x && this.y==c.y && this.radius==c.radius;
}
@Override
public int hashCode() {
int result = 17;
result = 31 * result + Float.floatToIntBits(x);
result = 31 * result + Float.floatToIntBits(y);
result = 31 * result + Float.floatToIntBits(radius);
return result;
}
public static Builder builder() {
return new Builder()
.setRadius(10); // default radius is 10
}
public static class Builder {
private float x;
private float y;
private float radius;
public Builder setX(float x) {
this.x = x;
return this;
}
public Builder setY(float y) {
this.y = y;
return this;
}
public Builder setRadius(float radius) {
this.radius = radius;
return this;
}
public Circle build() {
return new Circle(x, y, radius);
}
}
}
That’s a lot of code right? A lot of places to make changes if we need to, which could lead to bugs. IntelliJ/Android Studio does provide some generator functionality to help initially, but later in this post I’m going to talk about some libraries to help generate and maintain this code automatically.
The example above demonstrates a few things:
- A non-public constructor which takes all the values as parameters, allowing them to be
final
. - Getters for access to (in this case all) property values.
- Immutability of the values and therefore instance.
- Override
hashCode()
implementation to support use in Hash collections (e.g. HashMap) - Override
equals()
to allow comparison with other objects of the same type (works with hashCode() when used in hash collections). - Builder pattern to construct a new account (with default value for balance).
Where have I seen this? If you don’t think you’ve come across many examples of immutable classes before you would be wrong. Take for example the extremely popular JodaTime date library. The main classes in JodaTime are immutable. That is, if you want to take a date and add on 3 days, that function will return you a new date object, not modify the original.
Immutable collections prevent the object accessing your list of items from modifying it externally.
Equality
When you run a regular .equals()
on two objects in Java the default behaviour is to compare the memory addresses to see if they match, i.e. to see if they are physically the same object being pointed to. With value objects it’s not the memory location that’s important, it’s the structure (the values). To get this to work we need to override and implement the equals method ourselves – testing each property’s value against that of the comparison object.
As well as equals()
, we should also implement our own hashCode()
and toString()
methods. hashCode()
allows us to use our objects as the key things like HashMaps
. If you forget to do this you may experience problems at runtime, something like you’re able to add the same object twice to a HashSet
or are unable to remove elements from a HashMap
.
Android
Value classes are especially useful in Android because more often than not objects need to be marshalled between processes or Activities (to those not familiar with Android, you cannot “directly” pass an object between one Activity (think screen) to the next, either by reference or value. It must be serialized and passed in a Bundle called “extras” which is a package of serialized values that can be pulled out and reconstructed on the other end.
Some options are:
- First store the object on disk, pass a simple data type, the ID for example, and construct the object again from a persistent store, like a SQLite database.
- As above but store the data in a “global” scope (e.g. the Application or a Singleton) and retrieve it by ID from that scope. Leaky.
- Serialize the object. This can be relatively automatic by implementing the Serializable contract, by implementing the
Parcelable
interface, or something custom (e.g. JSON).
Generally the Parcelable
option is recommended as it is performant, avoiding reflection, the downside is it should not be used for persistence (you can have versioning issues).
Libraries
Using libraries (annotation processors) we can greatly reduce the amount of boilerplate required to write these objects. Less code generally means less potential for bugs. Take for example the scenario where an API change means that you want to add a new field. Now add in the getters, setters, modify hashMap, equals, toString, update your tests to make sure there’s no regression. Don’t slip up. Stop! Why not let a fully tested library write this code for you…
Project Lombok
I originally looked at Lombok because at the time I wasn’t looking for a value object library per se, I was just looking for an annotation processor that could generate getters and setters for my API model classes to save me writing lots of boilerplate code, and Lombok does just that – and more.
In a nutshell Lombok provides a bunch of annotations like @Data
, @Getter
, @Setter
, @ToString
, @EqualsAndHashCode
and @RequiredArgsConstructor
which all generate concrete methods based on the properties you annotate.
The most general of these annotations is @Data
…
@Data
class Account {
private long id;
private float balance;
@SerializedName("account_owner")
private AccountOwner accountOwner;
}
How’s that for contrast, just a couple of annotations and we get a raft of generated code happening behind the scenes.
When Lombok runs the @Data
annotation your class will be adorned with getters and setters for the above fields, a related hashCode()
, toString()
and equals()
method. Each method is annotated with a @Generated
annotation so you know what’s new. I’ve also thrown in an @SerializedName
annotation to show how you’d instruct GSON to look for a JSON key of “account_owner” for the Java accountOwner
field. You can add your own custom methods in too with @Keep
.
Now you can be a lot more fine-grained, using some of the other annotations on offer, and many also accept parameters to further define what sort of code will be generated, such as restricting access levels, making a setter protected
, for example.
Lombok requires you to install a plugin to Android Studio and the following module dependency in build.gradle:
provided "org.projectlombok:lombok:1.12.6"
Lombok is extremely powerful and easy to use but I hit a couple of stumbling blocks. The first one being that you cannot easily make a class Parcelable
– and I had a *lot* of these data classes to deal with so I wanted a way of making this as painless and maintainable as possible. There is a fork called hrisey but I tend to avoid anything too far off the master branch because it almost invariably gets behind or abandoned and the last thing you want is to upgrade your project and have to remove Lombok throughout.
I started to think that I might prefer to keep my classes as pure value objects, remove the setters. I never needed to change the values and they didn’t have any kind of identity. Thanks to Fragmented podcast and a talk at DroidCon UK I’d heard about AutoValue and a set of extensions which included automatic Parcelable
support, so I took the leap.
AutoValue
Unlike Lombok, AutoValue is squarely focussed on creating value objects, you cannot make fields settable, you cannot even subclass easily (more on that later).
Let’s start with an example:
@AutoValue
public abstract class Address implements Parcelable {
@SerializedName("first_line")
public abstract String getFirstLine();
// ...
@SerializedName("postal_code")
public abstract String getPostalCode();
@SerializedName("address_confirmed")
public abstract boolean getAddressConfirmed();
public static Builder builder() {
return new AutoValue_Address.Builder();
}
@AutoValue.Builder
public abstract static class Builder {
public abstract Builder setFirstLine(String firstLine);
// ...
public abstract Builder setPostalCode(String postalCode);
public abstract Builder setAddressConfirmed(boolean addressConfirmed);
public abstract Address build();
}
public static TypeAdapter typeAdapter(Gson gson) {
return new AutoValue_Address.GsonTypeAdapter(gson);
}
}
So there’s obviously more code than the Lombok sample. First of all we have the @AutoValue
annotation which invokes the annotation processor, generating at least one subclasses (AutoValue_Address).
You might note that we only define the accessors. The name of the method (minus the “get” or “is”) is what AutoValue uses to define the private fields that will store the values.
I said above that it generates at least one subclass because AutoValue has extension modules that generate intermediary subclasses named with $
symbols, e.g. AutoValue_Address$
. The Parcelable
interface invokes one such extension, autovalue-parcelable. Each subclass adds the respective functionality. When you are writing code you only ever reference Address
primarily because you are constructing your instances with the generated Builder.
The above example shows how a Builder is defined with the @AutoValue.Builder
annotation. The abstract setter and build methods are used to construct a concrete implementation of the Builder, and the processor will throw a compile-time exception if you miss any out for any field accessors not marked as @Nullable
.
The final part shows a custom TypeAdapter
used in GSON deserialization. This is another AutoValue extension and is unfortunately necessary bloat because otherwise GSON would not be able to construct the value objects (via Builder or constructor initialization).
One more extension worth looking into is AutoValue With extension which provides a handy way of constructing new value objects based on existing ones. e.g. Address newAddress = oldAddress.withPostalCode(newPostalCode);
which is great if you just need to change a field or two (perhaps a user is editing the data).
Subclassing
With AutoValue your classes are marked as abstract
(so that it can generate a full-bodied instantiable sub-class) and intentionally does not support inheritance of value object classes in order to conform with Item 8 of : “There is no way to extend an instantiable class and add a value component (field) while preserving the equals contract.”.
It is actually possible to do this, you need to ensure that your super classes are not annotated with @AutoValue
, only the subclass. You also need to make sure your Builder has setters for all of the properties including those of any base classes. Not perfect, but when you have guaranteed commonality across a lot of classes, it does work – but it’s not recommended for the reasons given above.
Conclusions
Ultimately I decided to settle on AutoValue for several key reasons:
- It’s a Google Project, which in general means it’s well documented and supported.
- Extensions. There are some useful AutoValue extensions to support things like making your classes Parcelable, support for GSON parsing, and writing “with” factory methods (create a new instance that’s the same as the current one, but “with” the following differences e.g.
Person twin = boy.withIdAndGender(anotherId, Gender.FEMALE);
- It does not modify existing classes (an annotation processor should generate new files), nor require an IntelliJ/Android Studio plugin to perform the voodoo that dynamically generates code in your existing classes (Lombok’s Android support is somewhat more complex).
Further reading
Entity vs Value Object: the ultimate list of differences
By Vladimir Khorikov – http://enterprisecraftsmanship.com/2016/01/11/entity-vs-value-object-the-ultimate-list-of-differences/