An interface provides a level of indirection between a client (a class that uses the interface) and the implementation details of a concept. For example, if we wanted to create a persistence layer that stores data in a database, we could simply create a class that performs the persistence to a specific type of database (say, A). The problem is that the rest of our system that uses the class for database A is now tied to the implementation details of database A.
If we decide later to use database B, swapping out the A class for the B class may be difficult. If, instead, we created an interface, Repository
, that abstracts the common details of both databases A abd B, we could provide a simplified facade to persisting objects. Then we can create an implementation of Repository
for A and B:
public interface Repository {
public void save(Object object);
public Object findById(long id);
}
public class ADatabaseRepository implements Repository {
@Override
public void save(Object object) {
// ... save object in database A ...
}
@Override
public Object findById(long id) {
// ... find object with id in database A ...
}
}
public class BDatabaseRepository implements Repository {
@Override
public void save(Object object) {
// ... save object in database B ...
}
@Override
public Object findById(long id) {
// ... find object with id in database B ...
}
}
Then some client class, Client
, can depend on the interface Repository
, rather than on some specific implementation class:
public class Client {
private final Repository repository;
public Client(Repository repository) {
this.repository = repository;
}
public void doSomething() {
Object desiredObject = repository.findById(1);
// ...
}
}
This allows us to supply any implementation of Repository
to Client
at runtime without changing any code in Client
:
ADatabaseRepository a = new ADatabaseRepository();
BDatabaseRepository b = new BDatabaseRepository();
// Create with A
Client clientA = new Clinet(a);
// Create with B
Client clientB = new Client(b);
Warning: Although interfaces are very useful, they can be overused. If there is a situation where indirection is not needed, such as when there will likely only be a single implementation of some interface, then a class can be used in place of an interface and class (although the decision to do so is a judgment call; there is no hard-and-fast rule). Using an interface where a class would do creates unneeded complexity.
For more information on common use cases for interfaces, see the following: