A common part of any application is to save data, altough there can be a big difference in the reason why you are saving data. Maybe it is just the output of the application which takes a certain input, processes it and writes some output again. But a lot of applications save data to have some kind of memory in case it is shut down, or in case of a failure. A lot of applications wouldn’t be thinkable without any way of storing data for later usage, even simple ones. I recently encountered this with the GroceriesInsight application, which currently doesn’t do much, but even then I already felt the pain of not being able to save the data.
Because of this saving data is a well known process that holds little secret. The first thing you would decide would be in what format you want to save your things, this can vary from a text format such as XML, JSON to a database. Once decided you will search for a library that makes it easy for you to write the data in a correct format. But then you are faced with how to get your data from the object to the library to save it, this problem will the be topic for this blog post.
Popular libraries for Java are Hibernate and JAXB, for database storage and XML respectively. These libraries integrate directly into your object to save it directly in the correct format, solving the problem of how to get the data from the object into the format. But there are a couple of pitfalls when using these libraries.
I have seen a lot of bad usage for these libraries, where you would have a separate class for the Hibernate, one for the JAXB and another one for the actual object. This means that getting the data from the actual object into the ‘special’ data object to save it is still a cumbersome job and does rely on the availability of getters and setters.
It is absurd you have to provide getters (but even worse) setters for this reason, as by doing you lose all control over the internal state of the object and it becomes too easy to change the state of the object without any checks. It would make a lot more sense to directly plug this into the object itself, which is possible by using annotations. But even then there are certain limitations to what the frameworks can do.
For instance, Hibernate requires a package default constructor, and of course all of the annotations on your members. You can see that if you want to support multiple ways of saving your data you could end up with a very messy class due to all the annotations, which you arent using anyway. Moreover, there is a mix of concerns here, as your object containing the application logic is now mixed with the logic on how to save the data.
In my opinion there should be a clear separation, you have the application object which contains the logic and the data, and you have something that determines how to save the object. It is however still the responsibility of the object to decide what needs to be saved. From an OO point of view this only makes sense, as the internals of the object (what has to be saved) should only be known to the object, but at the same time the object should not concern itself with how that data will be saved.
What I used to do in the past, was letting the object ‘save itself’. While this ofcourse could not be done completely, I did let the object create the SQL statement itself. This way it was the object that decided what needs to be saved, and it did not had to expose any of its internals, by simply passing on the SQL statement to an executionar nothing could get messed with. While I do consider this to be an improvement over copying all date in a different object, this is still not the ideal situation as the object needs to know the format in which to save itself. If I ever wanted to change the format or support a different one, I would have to change all of my objects to support this new format.
The idea I came up with recently, is similar as what can be done with (programming) languages. Let’s say you have N languages, and you want to be able to translate all of them into any other language, then you require N x (N – 1) converters. If a new language is added then you need to provide a convert for that language to all other languages and you need a converter for all other languages to the new one. A simple way to overcome this problem is to have a ‘master language’, the master language serves as an intermediate step into which all languages are translated to and from.
This approach has the following benefits:
- It reduces the total amount of converts from N x (N – 1) to 2 x N
- When adding a new language, you only need to add 2 new converters. It does not require any work for the existing converters or languages, only for the ‘master language’.
Doing the same when saving an object would mean that an object would map itself to a certain structure, and this structure could then be converted into any desired output format. When you want to add a new format you only have to add a mapper from the intermediate structure onto the format, and would not require any changes to the objects themselves. Changing one of the formats as well would only impact that single mapper and nothing besides it.
I started to work on this such that I could use it in my GroceriesInsight application. Similar to the GrocerciesInsight application, I will write more blog posts as work progress and I am faced with new challenges.