In this section, we discuss using interfaces to define contracts. In Java, the interface is an abstract structure you use to declare a specific responsibility. An object implementing the interface has to define this responsibility. More objects implementing the same interface can define the responsibility declared by that interface in different ways. We can say that the interface specifies the “what needs to happen,” while every object implementing the interface specifies the “how it should happen.”
Suppose you use a ride-sharing app because you need to get somewhere. When you order a trip, you usually don’t care how the car looks or who the driver is. You just need to get somewhere. In my case, I don’t care whether a car or a spaceship comes to pick me up if I reach the destination in time. The ride-sharing app is an interface. The customer doesn’t request a car or a driver, but a trip. Any driver with a car who can offer the service can answer the customer’s request. The customer and the driver are decoupled through the app (interface); the customer doesn’t know who the driver is nor which car will pick them up before a car responds to their request, and the driver doesn’t need to know who they do the service for. Using this analogy, you can deduce the role of interfaces in relationship with objects in Java.
An implementation example: Say you implement an object that needs to print packages’ details to be delivered for a shipping app. The printed details must be sorted by their destination address. The object dealing with printing the details needs to delegate to some other object the responsibility of sorting the packages by their delivery addresses:

The DeliveryDetailsPrinter directly delegates the sorting responsibility to the SorterByAddress object. If we keep this class design, we may face difficulties later if we need to change this functionality. Let’s imagine you need to change the printed details order later, and the new order is by the sender’s name. You’d need to replace the SorterByAddress object with another one implementing the new responsibility, but you’d also need to change the DeliveryDetailsPrinter object that uses the sorting responsibility:

How can we improve this design? When changing an object’s responsibility, we want to avoid the need to change other objects using the changed responsibility. This design’s problem occurs because the DeliveryDetailsPrinter object specifies both what it needs and how it needs. As discussed earlier, an object only needs to specify what it needs and stay completely unaware of how the what is implemented. We do this, of course, by using interfaces. In figure 4.3, I introduced an interface named Sorter to decouple the two objects. Instead of declaring a SorterByAddress, the DeliveryDetailsPrinter object only specifies it needs a Sorter. You can now have as many objects as you’d like to solve the what requested by the DeliveryDetailsPrinter. Any object implementing the Sorter interface can satisfy the dependency of the DeliveryDetailsPrinter object at any time. Figure 4.3 is a visual representation of the dependency between the DeliveryDetailsPrinter object and the SorterByAddress object after we decoupled them using an interface.

In the next code snippet, you find the Sorter interface definition:
public interface Sorter {
void sortDetails();
}
Look at figure 4.4 and compare it with figure 4.2. Because the DeliveryDetailsPrinter object depends on the interface instead of the implementation directly, you don’t need to change it further if you change the way the delivery details are sorted.

Say you are implementing an app a team uses to manage their tasks. One of the app’s features is allowing the users to leave comments for the tasks. When a user publishes a comment, it is stored somewhere (e.g., in a database), and the app sends an email to a specific address configured in the app.
We need to design the objects and find the right responsibilities and abstractions for implementing this feature.
In standard real-world applications, we usually refer to the objects implementing uses cases as services, and that’s what we’ll do here. We’ll need a service that implements the “publish comment” use case. Let’s name this object CommentService. I prefer to give the service classes a name that ends with “service” so that their role in the project stands out.
When analyzing the requirement again, we observe that the use case consists of two actions: storing the comment and sending the comment by mail. As they are quite different from one another, we consider these actions to be two different responsibilities, and thus we need to implement two different objects.
When we have an object working directly with a database, we generally name such an object repository. Sometimes you also find such objects referred to as data access objects (DAO). Let’s name the object that implements the storing comment responsibility CommentRepository.