Encapsulate!

Encapsulate!

No developers were harmed in this article! The object names was changed to protect the developers involved.
I often see objects implemented as value objects (such as POJOs) that should be fully fledge objects instead! I have implemented a bunch of them myself over the years – but they usually infer problems like bugs and is hard to refactor.
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. Value objects should be immutable, this is required for the implicit contract that two value objects created equal, should remain equal.
Here is a simple example with an Object that has a List with objects which is exposed through a getter and a setter.
public class MyObject {
   private List<ListObject> objects;

   public List<ListObject> getMyObjects() {
      return objects;
   }

   public void setMyObjects(List<ListObject> objects) {
      this.objects = objects;
   }
}

It seems pretty harmless, but it has several flaws:
  • What is the intention of this object?
  • It is hard to test.
  • It is easy to create bugs
Let’s start with testing – testing this class will be a pain because we put all the logic outside of the class. And what are we testing – that we can get and set a list?
We should test the intention of this class, but it is unclear what this class does, it could be a collection of objects or try to act as a domain object. What is it’s constraints? Sure we could use JavaDoc to describe why this class exist or what it does, but what happens after a few changes and will the objects that uses this object hold it’s written contract in JavaDoc.
So what I found in my most recent example was that another object got the list of object through get and manipulated the object. MyObject could skip the getter and setter and make the List public, in this case it would be clear to everybody that this was OK behaviour (the intention from the developer).
myObject.getMyObjects().add(newObject);
Let say we find a problem with another object that is updating this object and accidentally adds duplicates (e.g the user inputs the value twice). We can try to solve it in the object that is manipulating this object or add a method that will check for null and duplicates.
public void addMyObjects(ListObject listObject {
   if ( listObject != null ) {
       if ( !this.objects.contains(listObject) )
         this.objects.add(listObject );
       }
   }
}

We can not remove the get/set without doing refactoring on other parts of the system using the class, if we don’t remove get/set methods next developer will see three different ways to add things to the object. But now at least we can test part of the object, but we can not guarantee the information is correct due to other objects access to the List. Depending on how entangled the code is with other parts can make the developer to add to the technical debt or decrease it by refactor the object.

Retrospective

Make your classes immutable if they are value objects.
Don’t allow other object change your data in your class directly.
Make it testable – even if you don’t write test for every function, it will make you think of your objects in a different way.
You can change the code a little bit to show your intent by not allowing consumers of the object to directly modify the list, e.g. by using an Enumeration, Iterator or Collection.unmodifiableList(). We could also return an Array.
public class MyObject {
private List<ListObject> objects = ArrayList<ListObject>();

public Enumeration<ListObject> getMyObjects() {
   // Return a unmodifiable list e.g. enumeration.
return new Collections.enumeration(objects);
}

public void addMyObjects(ListObject listObject {
if ( listObject != null ) {
if ( !this.objects.contains(listObject) ) 
   // Make a copy so it cant be manipulated
this.objects.add( new ListObject(listObject) );
}
}
}
}
Remember… If it doesn’t hurt, you are doing it wrong (wink)