Reason 1 - Development
This reason I learned, as it was mentioned in one of the books I studied when getting into Java (OCP Oracle Certified Professional Java SE 8 Programmer II by Jeanne Boyarsky and Scott Selikoff, page 51).
"An interface provides a way for one individual to develop code that uses another individual’s code, without having access to the other individual’s underlying implementation.” (might be good to replace individual with a team terminology 🙂 )
There are usually multiple teams working on the same project. Often, there are parts of the solution that are being developed simultaneously and depend one on another, so one team’s objects uses another team’s objects.
But how to base the development on objects that are not yet there? We define interfaces. Developer using the interface can create a temporary mock object (sometimes referred as dummy code). Mock object simulates the real object that implements the interface with a simple implementation. This allows developers using the interface to compile, run and test their code.
Reason 2 - Hiding implementation
This one I came across while learning Algorithms, courses provided by Princeton (Robert Sedgewick and Kevin Wayne).
Interface is a definition of a solution we offer to the client. It defines methods but doesn’t implement them. Classes that implement interfaces carry algorithms and data structures used.
We do this for 2 reasons:
Client shouldn’t and must not know about the details of the implementation. Client cares about which methods it can use, what is the time complexity and data usage behind it.
For example, let’s offer a solution for feeding animals on a farm. A framer has a lot of work, lots of farm animals that he needs to feed every day. He needs to be efficient. Farmer does need to know what to feed an animal with, and what is the sufficient amount of food that animal requires (arguments to a feed() method), but doesn’t need nor want to care about how fast animals eat and which sounds they make during.
Also, we wouldn’t want our farmer to know all the algorithms we developed, how feeding and other processes work in the background. If he would know that, he might pick up some programming skills and create his own solution. It would safe him some money.
Implementation doesn’t know about client needs. Libraries are often written for various clients. We might offer different implementations of the interface, where clients chose best suited for them, based on their needs.
Let’s take an example of a Graph. In order to maintain the Graph data structure we need to store vertices. For every vertex we need to know it’s adjacent vertex. We can offer a client three implementations, with different underlying data structures: list of edges, adjacency matrix or adjacency list. Which implementation best suits the client, depends on, e.g. which operations will be needed most often, will there be a large amount of data, making the matrix storage an issue, and so on.
Reason 3 - Testing
I learned this one in practice. 🙂
Let’s say we have two services. One providing us with users and another sending emails. We would like to test the service responsible for generation and sending emails. Content of the email depends on user’s details. It needs user’s name, email address,… Imagine we are testing generation of the email content. Since it includes user’s details, we can’t be sure if our test fails due to user service returning wrong user details or its generation of the email that acts faulty. For this reason we create a mock user service. Retrieving user details is now controlled and we can be assured that what we are testing is indeed composition of the email content, defined in the email service.
Let me know if you think of another purpose!