Use extension to avoid utility class proliferation
posted by Moandji Ezana
Neal Ford claims that "Lots of 'Helper' or 'Util' classes indicates a poor design" and suggests trying to "incorporate your helpers into an intelligent domain object." If the projects I've been on are any indication, his advice hasn't been heard, and people end up with, for example, a DateUtil class that provides calculations, formatting and all that other stuff that the JSE libraries are so terribly unwieldy for. I'm sure you've come across code like:
long DateUtil.calculateTimeBetweenDatesInSeconds(Date, Date)
and
String DateUtil.formatyyyyMMdd(Date)
or even (gulp):
date1.getTime() >= date2.getTime()
This kind of code is extremely common, but, let's face it, pretty ugly, not really object-oriented and, most importantly, difficult to read and error-prone. I'd be much happier writing:
boolean Date.isBefore(Date) [No more "Which date goes first, again?"]
String Date.formatyyyyMMdd()
long getIntervalInSeconds(Date)
So, here's an attempt to apply Ford's advice.
Begin by extending java.util.Date and copy the truly necessary DateUtil methods (I've found that utility classes have a tendency towards bloat) to it. That's pretty easy, but it's when you try to integrate your fancy new MyExtendedDate with clients and frameworks that things get a little trickier.
You could simply change the type of the domain object's field and modify the getters and setters [1]. However, if you're using an ORM framework, it might not be too happy about that, as it doesn't know MyExtendedDate. You'd have to teach it about your class. For example, with Hibernate you'd use a custom type converter. If you're using Struts2 and navigating your object graph with OGNL [2], you may have a similar problem, which you'd resolve in a similar way.
If you're mapping getters and setters directly onto your fields [3], your new setter will break clients. You could simply change setDate(MyExtendedDate) back to setDate(Date), because the subclass doesn't add any state, only behaviour, then create a new MyExtendedDate instance based on the date passed in. Or, if possible, keep setDate(MyExtendedDate) and change all its clients. Or have both and mark the former deprecated. The ideal solution, of course, would be getting rid of the setter altogether [3].
If you can't change the type of the field, you could change getDate() to return MyExtendedDate, which has the advantage of not breaking clients. If you can't change the field's type, you probably can't get rid of setDate(Date), either, but clients can give it a MyExtendedDate anyway, so it's not too much of an issue ([3] notwithstanding).
All that now remains is to find all the calls to DateUtils, replace them with MyExtendedDate's methods and eventually get rid of DateUtils. If you can't remove DateUtils right away, you could at least deprecate it and reimplement its methods to call the corresponding ones on MyExtendedDate.
Voilà: nice, smart and object-oriented Dates, at your service! If you're interested in more ways to improve your code without breaking everything, I highly recommend Michael Feathers' book Working Effectively With Legacy Code. It's like an extension to Martin Fowler's Refactoring that deals with all those real-world situations that make your eyes bleed.
[1] Getters and setters are evil, of course, but that's another topic, see Peter Gillard-Moss for a possible alternative. The last line, paraphrased, sums it all up nicely: "The trick with maintaining your encapsulation as neatly as possible is to try and ensure that [you] deal with the concepts of [the] domain (for example IStatistic) and not the structure of the data (e.g. int speed)."
[2] If you have a big domain model, I highly discourage this, as it leads to unrefactorable and tightly-coupled code. Another topic for another time.
[3] Remember: getters and setters are evil! :)
long DateUtil.calculateTimeBetweenDatesInSeconds(Date, Date)
and
String DateUtil.formatyyyyMMdd(Date)
or even (gulp):
date1.getTime() >= date2.getTime()
This kind of code is extremely common, but, let's face it, pretty ugly, not really object-oriented and, most importantly, difficult to read and error-prone. I'd be much happier writing:
boolean Date.isBefore(Date) [No more "Which date goes first, again?"]
String Date.formatyyyyMMdd()
long getIntervalInSeconds(Date)
So, here's an attempt to apply Ford's advice.
Begin by extending java.util.Date and copy the truly necessary DateUtil methods (I've found that utility classes have a tendency towards bloat) to it. That's pretty easy, but it's when you try to integrate your fancy new MyExtendedDate with clients and frameworks that things get a little trickier.
You could simply change the type of the domain object's field and modify the getters and setters [1]. However, if you're using an ORM framework, it might not be too happy about that, as it doesn't know MyExtendedDate. You'd have to teach it about your class. For example, with Hibernate you'd use a custom type converter. If you're using Struts2 and navigating your object graph with OGNL [2], you may have a similar problem, which you'd resolve in a similar way.
If you're mapping getters and setters directly onto your fields [3], your new setter will break clients. You could simply change setDate(MyExtendedDate) back to setDate(Date), because the subclass doesn't add any state, only behaviour, then create a new MyExtendedDate instance based on the date passed in. Or, if possible, keep setDate(MyExtendedDate) and change all its clients. Or have both and mark the former deprecated. The ideal solution, of course, would be getting rid of the setter altogether [3].
If you can't change the type of the field, you could change getDate() to return MyExtendedDate, which has the advantage of not breaking clients. If you can't change the field's type, you probably can't get rid of setDate(Date), either, but clients can give it a MyExtendedDate anyway, so it's not too much of an issue ([3] notwithstanding).
All that now remains is to find all the calls to DateUtils, replace them with MyExtendedDate's methods and eventually get rid of DateUtils. If you can't remove DateUtils right away, you could at least deprecate it and reimplement its methods to call the corresponding ones on MyExtendedDate.
Voilà: nice, smart and object-oriented Dates, at your service! If you're interested in more ways to improve your code without breaking everything, I highly recommend Michael Feathers' book Working Effectively With Legacy Code. It's like an extension to Martin Fowler's Refactoring that deals with all those real-world situations that make your eyes bleed.
[1] Getters and setters are evil, of course, but that's another topic, see Peter Gillard-Moss for a possible alternative. The last line, paraphrased, sums it all up nicely: "The trick with maintaining your encapsulation as neatly as possible is to try and ensure that [you] deal with the concepts of [the] domain (for example IStatistic) and not the structure of the data (e.g. int speed)."
[2] If you have a big domain model, I highly discourage this, as it leads to unrefactorable and tightly-coupled code. Another topic for another time.
[3] Remember: getters and setters are evil! :)
Labels: dev

