I need to butt heads with conventional coding wisdom for a minute. We've all heard that it's good to program to interfaces and not implementations, but I'd like to argue that in a lot of cases I see nowadays, it actually buys us very little. In fact, it often just causes code bloat and developer frustration. There, I said it!
Here's the picture I see so often:
Note that implementations only depend on interfaces, and the actual implementation that realizes the interface is injected via some IOC container (e.g. Spring) or found via some Service Locator. For example, the CustomerAction uses a CustomerManager, but which CustomerManager implementation it doesn't have to know or care about.
This is all done in the name of reduced coupling, since we all know that loosely coupled systems are more maintainable and extensible than tightly coupled systems. But is loose coupling always good?
The benefits vs. the reality of programming to interfaces
Well, indeed there are some benefits to programming to interfaces and not implementations, but in many cases I think they just don't apply. Here are some of the touted benefits, and the reality that negates them.
Benefit: Coding to an interface allows you to easily substitute implementations whenever the mood (or the requirements!) strike you. If for some reason you need to swap CustomerManagerImpl with BigCustomerManagerImpl, you'd be sitting pretty. You'd just change the Service Locator or IOC config file/class to use BigCustomerManagerImpl instead of CustomerManagerImpl, and never have to bother updating any client code. Voila! With one class and one config file changed we have all new behavior. Reality: Unless you have a reasonable expectation that you'll need multiple implementations, this benefit really holds no water. Essentially, why decouple two things from each other if it's likely that they'll always be coupled? For DAO and Manager-type classes, this is especially true. Sure someone will argue "if we program to DAO interfaces rather than implementations, when we switch from Oracle to DB2 we'll never need to touch our business logic!". The question to ask, however, is, "is this a reasonable possibility"? In most enterprise environments, the probability of swapping databases so low that it's irrational to spend any time or effort attending to it (see The Architect's Dilemma). Ditto for business manager classes. Really, what other types of CustomerManager implementations could we imagine? In the end, I'd argue that it's usually pretty clear when multiple implementations will be possible - and in business applications this is most often in the domain model (e.g. Account has children: SavingsAccount and CheckingAccount), business Strategy or algorithm-type classes, or frameworks and other infrastructural components. Benefit: Creating stub classes (e.g. StubCustomerManagerImpl) allows me to better isolate the system under test, enabling better testability. For example, if I was unit testing the CustomerManagerImpl, I could create and inject a StubCustomerDAOImpl so that I'm not testing multiple things. Reality: In practice, this benefit is pretty negligible since newer mock-object frameworks (e.g. EasyMock, JMock, etc.) can create stubs and mocks for classes without interfaces. Benefit: Some purists might argue that creating a CustomerManager interface is cleaner and more maintainable/readable - all the methods are right there in the interface, with none of the obfuscating implementation details. Reality: With modern IDEs, I find this argument tenuous at best - if I want to know the public methods, I'll probably rather just look in Eclipse's Outline view. Benefit: Coding to interfaces enables integration with other subsystems and modules. Clients which call your CustomerManager remotely, for example, need only include (i.e. in a dependent JAR) the interface, and not the implementation. Again, this allows your implementation to change without having to rebuild and redeploy all the clients (assuming the contract hasn't changed, of course). Reality: No counter-argument, but this obviously only pertains to the API for your system. |
Even further, there are some very real (although admittedly relatively minor) costs to coding to interfaces as well: an increase in the amount of code we have to maintain, it's harder for developers to step through code (e.g. F3 in Eclipse won't work!), and there is an additional step for adding or modifying a method in a class (i.e. change it first in the interface and then the impl. ugh.).
Conclusion
In the end, I'm not saying that programming to interfaces and not implementations isn't a good thing, just that it's a good thing less often than we think - in other words, it can't just be dogmatically applied. Again, unless there's a reasonable expectation that another implementation underneath the interface will be needed, I say don't bother.
Do you agree? Disagree? Am I missing something? (I'm sure I am!). Let me know what you think.