The last few weeks I have been working on an application for one specific customer. The application is sold as is and will not need any new features after release, nor will it be used for another customer. This combined with the pressure of a very tight deadline is not a very good motivation to write clean code, but nevertheless I always think carefully about my design, and this was a chance I never had before. Writing a real-life application from scratch.
While the code I was writing was not perfect, and there was a lot of similar code I had to tackle to prevent a lot of duplication my team lead felt he had to intervene because the class was getting too big for him. With a total length of 700 lines the class was indeed getting a bit too big and cleaning it up was certainly on my list of things to do, but I never had the chance to do it the way I felt like it should.
One of the ways my team lead reduced the size of the class was tearing out code, code that made up the core of the class, and put it in a new class. Because this new class did not have any of the fields of the old class, he made a lot of getters and setters in the old class. This action lead to two things:
- Instead of a lot of similar (nearly duplicated) code in one class, we now had a bunch of similar (nearly duplicated) classes.
- My well designed class ended up with a lot of getters and setters making it a data object.
When the refactoring was done he asked my opinion of the result. When I confronted him with my disgust of this class that become a data object, and that it breaks the entire idea of object oriented programming he simply replied that it was not. I was literally shocked that a person with 30 years of experience in software development (of which 20 years with Java) just said that a data object is not a problem in object oriented programming.
Two of the main ideas of object-oriented programming is that of abstraction and encapsulation. The object itself contains all the data it needs to perform its responsibilities and it has a clear API that does not expose the inner details. So why does a data object break with these two concepts?
- A data object does not provide any abstraction as the API exposes all the internal elements with all the getters.
- A data object does not provide any encapsulation as the object depends on others to provide it with the correct data through its setters.
A different way to look at objects, one that is often used for unit testing, is by defining the objects through its responsibilities. A data object can never have any responsibilities as it can not ensure the correctness of its data as it can be modified by anyone at anytime. The object can never make any mistakes, simply because he is not the source of the wrong data.
Next to the moral objections, data objects make debugging your code a lot harder as well. If the application crashes for some reason and the stacktrace shows that the object is in an inconsistent state it will be hard to find out why this is so. Finding out why the value changed can not be answered without finding out which other object changed the value.
In my opinion data object should be avoided, and the tendency to use must be a sign to rethink the design of the software. However, I have seen many data objects and I know that in some cases there is no other choice but to have a data object. One of the easiest and most straight-forward examples is a data object that is used in a user interface. The object requires a lot of getters such that the data can be shown, and it needs setters to allows the users to change it. While I am typing I realize that even this can be avoided if we re-design the user interface such that the object itself will show the details on the screen and handle any changes made.
Perhaps a much better one is an object that stores general system information, information that should be available to each component of the software. But even this should not be a complete data object as the data should probably not be directly modifiable. While the need for data object may sound obvious and may even look required in many cases, it often can be solved in a different way that prevents or at least minimizes this need.