Do methods belong in an interface or in a utility class? This post describes my vision about this question.
A few weeks ago there was a discussion on a blog about the oo’ness of utility classes. Some people claimed that utility classes are not object oriented, because the static utility method is not executed on the object itself. Instead, this object is passed as an argument to a utility-method. I think this view limits good oo-design, because calling someObject.someMethod() is not the holy grail of object oriented programming (just like subclassing).
2 Different types of methods
For this discussion I distinguish 2 different types of methods:
- implementation dependent methods like List.size(). The size method depends on a specific implementation of the List interface, how it is determined; maybe an internal counter is used, maybe an iteration over the elements of the list is done.
- implementation independent methods. These methods are build on top of other interface methods, without having any implementation specific knowledge. An example of such a method is the List.indexOf(Object). This method can use the List.get(int) and the List.size() methods to find the first index. There is not always a clear separation which methods are dependent and which ones are not; you could always create a List implementation where the indexOf method uses a hashtable for better search-performance.
The best location for an implementation dependent method is adding it to the interface. If it is placed outside that interface, you would have to break encapsulation, and in most cases this is something you want to prevent. The implementation independent method has more options of being placed:
- add them to the interface. An example is the already mentioned List.indexOf(Object) method. A technique often used, is creating an abstract super class that contains the implementation independent methods. The subclass only needs to implement the implementation dependent methods. An example of this technique is the AbstractList and the ArrayList. The ArrayList extend the AbstractList and only needs to implement the implementation dependent methods like get(int) and size().
- add them to a utility class. An example is the Collection.sort(List)
Which location is best
The question remains which location is best. Like most interesting questions, there is no black and white answer. If you add a new method to an interface, it could be that you are breaking implementing classes; especially when the interface is very public and you don’t have control over the implementations. Although using an abstract superclass can prevent some damage, you have no way of knowing if a class implements the interface directly.
Methods in utility classes don’t have this problem, because adding a new utility-method won’t break any implementations. Another important advantage of using a utility class is that it helps to prevent bloated interfaces. You can even use multiple utility classes to group related methods. I must confess that this technique is not something I have used very often, but on certain occasions it was very helpful. But utility classes are not without problems either; they are more difficult to use because it is less obvious where functionality can be found. The lack of code completion for these methods doesn’t help either. The last disadvantage I can think of is that they can’t make use implementation specific functionality.
Which location is best, needs to be decided on a case by case basis. But in a lot of cases interfaces are quite simple and adding new methods to the interface is often the easiest solution.