This is a small tip containing my thoughts on why we should not blindly create interfaces and/or abstract class for each and every class in our application. These are more of thoughts and some people may not agree with my school of thought but the idea here is to share my opinion with others so that others can share their thoughts and I/we will get to learn from them.
The other day I was having a discussion with someone when I suggested abstracting out a 3rd part library calls in a class so that the calling application can only depend on this class. The rational behind this suggestion was just to have a single point which is communicating with the 3rd party library rather than having the calls to this library scattered all over the application code. In future if we decide to use another 3rd party library, only the internal implementation of this class needs to change and the callers will remain unaffected.
The solution to this was designed in such a way that that the new implementation contained an interface, an abstract class implementing this interface and a concrete class containing the actual calls to the library. Now in normal circumstances, I would have said that this is an elegant solution since we have the Dependency inversion principle being followed here with proper interface and all. But this time it made me think whether we really need the interface and abstract class.
I am totally in favor of using interfaces, abstract class and dependency inversion principle. Please read the following articles to view my take on them:
- Program to Interface, not Implementation – Beginner’s Tutorial for Understanding Interface, Abstract class and Concrete class[^]
- An Absolute Beginner’s Tutorial on Dependency Inversion Principle, Inversion of Control and Dependency Injection[^]
But sometimes creating an interface and abstract class might be an overkill. Lets see why.
Why we need an interface
Interface is needed mostly to define a contract. The callers can use the interface and choose to select any concrete implementation fulfilling this contract by either letting the concrete implementation getting injected or by using a factory or service locater. The important thing to understand is that “caller can choose” is the real deal here. Now imagine a scenario like validating an email format. Will there be any scenario where the caller need to selectively choose a class providing this functionality. If the answer to this question is “No”, then why do we need an interface. Why can’t the caller use this class directly.
So how to decide whether we should or should not create an interface. Well the answer is when we want to define a contract that can be fulfilled by multiple concrete classes and these multiple concrete classes can coexist in the application and the caller will be calling them selectively based on some conditions. If we have a scenario where it is not at all possible to have multiple concrete implementation co-existing in an application, perhaps creating an interface is not needed and if we do it will be an overkill.
The point here is that we should not be creating interfaces for every class just for the sake of creating them. There should be a justifiable need to create an interface. Also, extensibility and flexibility should be considered when choosing to create an interface. But if we are fairly certain that we don’t need to enforce a contract and its not possible to have the multiple concrete implementation to coexist, perhaps we should not create an interface at all and use the concrete class directly.
Why and when we need an abstract class
First things first, abstraction and abstract class are different things. Abstraction is what a class provides to its callers and abstract class is a language construct. The question here is when should we create an abstract class. The answer to that should be – whenever we have the possibility of multiple concrete implementation to coexist and there is some default and/or common behavior required for these concrete implementations, we should create an abstract class and put these default/common behavior in this abstract class. This abstract class will now implement our interface and the concrete classes will inherit from this abstract class.
Point of interest
Using object oriented principles and practices is essential to create good software. But we should first understand the rational behind why these practices exist and what problem these patterns are trying to solve. Then only we should start using them in our applications. Otherwise if we start using these practices without even having a need for them, the resulting design/code could be an overkill.
We should try to remember KISS(Keep it Simple, Stupid) and at the same time analyze the need of the applications and then start designing our solution accordingly. And this is valid not just for the interfaces and abstract class but also for design and architectural patterns. We should not be including patterns in our design just for the sake of using them but there should be a justifiable need for their use and then only we should use it.
This has been written from the beginner’s perspective. I hope this has been informative.