Unbeloved Constructors, Beloved Anemic Objects


Published: 2014-09-01
Updated: 2014-10-09
Web: https://fritzthecat-blog.blogspot.com/2014/09/unbeloved-constructors-beloved-anemic.html


As one can see in many project teams, the normal way to write a Java class is this: This is done on a large scale, most projects are full of such classes. They are justified by the Java Bean concept, and a lot of tools and libraries that rely on java.lang.reflect even need them, especially dependency-injection tools (DI). Rarely they get JavaDoc, they are said to be "self-documenting" :-)

Nevertheless such classes lack encapsulation and logic. They are called anemic objects (which is the name of an anti-pattern).

The following thoughts are around using constructors instead of the ubiquitous setters that endanger integrity at any time and from everywhere. Moreover I try to split such classes into a readonly- and a read/write-layer.



Here is a typical anemic class:
/**
* The inevitable ubiquitous undocumented anemic object, generated by some IDE.
* Any property can be modified from outside. This class encapsulates nothing.
* Public setters, once released in a big project, might be called by reflection
* and are not disposable anymore without risk.
*/

public class Contact
{
private String personName;
private boolean inCountry;
private String phoneNumber;
private String realm;
private int ranking;

public String getPersonName() {
return personName;
}
public void setPersonName(String personName) {
this.personName = personName;
}
public boolean isInCountry() {
return inCountry;
}
public void setInCountry(boolean inCountry) {
this.inCountry = inCountry;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getRealm() {
return realm;
}
public void setRealm(String realm) {
this.realm = realm;
}
public int getRanking() {
return ranking;
}
public void setRanking(int ranking) {
this.ranking = ranking;
}
}
This is also a problem when programming quickly via auto-complete in an IDE like Eclipse. The mass of names that show up when searching for a method is a real show-stopper. We'd liked only the really needed methods to be public!

So how can we do better? What about this variant:
/**
* An immutable value-object (data-transfer-object).
* No property can be modified from outside. This class encapsulates read-only data.
* Quick to read, easy to understand, safe against abuse, well-documented.
*/

public class Contact
{
/** The person this contact aims at. */
public final String personName;
/** True when the person is easily reachable in country. */
public final boolean inCountry;
/** The phone connection to the person. */
public final String phoneNumber;
/** The area of expertise the person can be asked about. */
public final String realm;
/** High when the person is a real expert in realm, low otherwise. */
public final int ranking;

public Contact(
String personName,
boolean inCountry,
String phoneNumber,
String realm,
int ranking)
{
this.personName = personName;
this.inCountry = inCountry;
this.phoneNumber = phoneNumber;
this.realm = realm;
this.ranking = ranking;
}
}
Much shorter, well-documented, much more encapsulation, less runtime-risks: an immutable object!
Dependency injection tools might not like that variant, but human beings do.

Mind that normally it is a very bad practice to create public fields (make it private and provide a getter). But in this case the final access modifier ensures that the field is not changed any more after construction.
So, isn't this a reason to love constructors?

But lets go a little further and try to find a way to define an immutable value-object, and, as a derivation of this, a class that adds write-access.


The two-layer object and its constructors

For a field to be writable from a sub-class it needs to be either protected or package-visible. I decided for the package-visible variant here, because I wanted to force any class that provides write-access to be in the same package.

Here is the readonly-layer:

package sample;

/**
* An immutable value-object (data-transfer-object).
* No property can be modified from outside. This class encapsulates read-only data.
* Package-visible fields prevent access from sub-classes outside this package.
*/

public class ContactData
{
String personName;
boolean inCountry;
String phoneNumber;
String realm;
int ranking;

/** Read-only constructor. */
public ContactData(
String personName,
boolean inCountry,
String phoneNumber,
String realm,
int ranking)
{
this.personName = personName;
this.inCountry = inCountry;
this.phoneNumber = phoneNumber;
this.realm = realm;
this.ranking = ranking;
}

/** Facilitate a write-access sub-class in same package. */
ContactData() {
}

/** @return the person this contact aims at. */
public String getPersonName() {
return personName;
}
/** @return true when the person is easily reachable in country. */
public boolean isInCountry() {
return inCountry;
}
/** @return the phone connection to the person. */
public String getPhoneNumber() {
return phoneNumber;
}
/** @return the area of expertise the person can be asked about. */
public String getRealm() {
return realm;
}
/** @return high when the person is a real expert in realm, low otherwise. */
public int getRanking() {
return ranking;
}
}
As we can see, this is immutable, no setters are present. Fields are not final, but well encapsulated. In the public constructor you could assert certain values to be not null.
But: dependency injection tools will not be satisfied, because setters are missing!

So here is the write-layer:

package sample;

/**
* A mutable data-transfer-object that provides write-access to all properties.
* In fact this does not encapsulate anything, but it reuses its super-class.
* So you can decide later whether a write-layer makes sense.
*/

public class ContactModel extends ContactData
{
/** For new objects getting values from UI. */
public ContactModel() {
}

/** For objects getting values from persistence. */
public ContactModel(ContactData data) {
super(
data.getPersonName(),
data.isInCountry(),
data.getPhoneNumber(),
data.getRealm(),
data.getRanking());
}

/** Sets the person this contact aims at. */
public void setPersonName(String personName) {
this.personName = personName;
}
/** Sets whether the person is easily reachable in country. */
public void setInCountry(boolean inCountry) {
this.inCountry = inCountry;
}
/** Sets the phone connection to the person. */
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
/** Sets the area of expertise the person can be asked about. */
public void setRealm(String realm) {
this.realm = realm;
}
/** Sets high when the person is a real expert in realm, low otherwise. */
public void setRanking(int ranking) {
this.ranking = ranking;
}
}
Now also dependency injection should be happy.
This class does not encapsulate anything, but for a data-transfer-object this is nothing peculiar.
The asset of this design is that we have two layers we can use separately. We could construct a read-only object and use it as data-transfer-object. We could then construct a read/writable object from that template when we need it for an editor.

I'm not saying that this is a solution for Command and Query Responsibility Segregation, but isn't it the same idea?


ɔ⃝ Fritz Ritzberger, 2014-09-01