Sometimes designers of interfaces get too elaborate, delving into layers of inherited interfaces and abstraction without actually writing any concrete implementations.
For example, look at the following definition:
public interface BaseEditableDao extends CommonResolvableDao
{
:
}
public interface MediaImageDao extends BaseEditableDao, BasePageableDao, BaseFilterableDao, BaseDao
{
:
}
public class DataProcessor
{
public void processRecord(BaseEditableDao record)
{
:
}
}
It's all fluff for the sake of creating interfaces and feeling important.
If the processRecord method expects a BaseEditableDao on its own, it's not obvious what kind of real objects might be passed into it. In theory we shouldn't care, but in practice there are too many interfaces with similar and confusing names that don't add enough value to justify their existence.
These interfaces might have been better defined as an abstract DefaultDao base class. A base class even provide some default implementations of methods to be overridden by subclasses.
A base class provides an ability to add useful framework methods to the type, such as a toString() method that does a debug dump of any kind of DefaultDao type, or a static instance counter that increments every time a new DefaultDao is created so you can track specific instances of them in the logs. If it was an interface you have no way of ensuring certain code quality or data consistency conditions are being met by your implementers.
public abstract class DefaultDao
{
:
}
“Roger coded away for weeks, building common libraries of classes and interfaces for the project. As a C++ programmer, he had excellent knowledge of the capabilities of Java and used the language to its fullest - abstraction. As an LDAP expert (where every object can be any other kind of object) he translated that concept into hundreds of very specific interface types. We only ever used about ten percent of them.
DomainObject, the main type to be used throughout the project was an interface which extended others, no concrete implementations in sight. Concrete implementations were for us to do, he said.
As the development of our services progressed, the code became a series of instanceof checks so we could determine which processing logic applied to different kinds of DomainObject. Every time we created a new object there were dozens of methods to create for all the interfaces we had to comply to, so the code quickly got out of hand and could not adapt to changes.
If DomainObject had been a simple Java class that implemented most of the basic interfaces it needed, everything would have been simpler. Or an abstract Java class. It didn't help that Roger's javadoc was non-existent but as an interface there wasn't a lot to get a grip on, especially when the interface was built from so many others.”
The danger is that with so many levels of indirection that the developer lives completely in an imaginary interface land. Especially when the interfaces are built into a separate project, it's hard to tell who might implement and use them.
When interfaces are passed around, it's more difficult to tell where the code does anything important or which methods are critical to implement. Sure, Javadoc can help to some degree but when one interface extends two others, which extend others it's like trying to catch an eel. At some point you need to bite the bullet and write some real active code.
Tip: There are only a few scenarios where it makes sense to spend extra time to create an interface:
When there is more than one implementation. For example, there will be many classes that implement Approvable and have an isApproved() method, making Approvable a good interface candidate.
When the interface is designed for public consumption as part of an API (e.g. HttpServletRequest)
When a single interface can provide a contract as part of a specific design, with the expectation that there will be multiple implementations for that contract. An example would be a TimesheetService that needs a real implementation and a mock testing implementation.