Card 03/ 05

ExampleDifficulty: Beginner1 min

Dependency Injection Example: A Mail Client Instead of a Database

The same constructor shape swaps a mail server, which shows it is a pattern and not a database trick.

Dependency injection, a class receiving what it depends on through its constructor instead of creating it, works for any dependency, not only a database. Here it is applied to a mail client that sends ride confirmations. The real client talks to an SMTP server (the standard protocol for sending email), and a stub, a stand-in that only reports what it was asked to do, prints the message instead.

java
interface MailClient {
    void send(String to, String body);
}

class SmtpMailClient implements MailClient {
    public void send(String to, String body) { /* talks to a real SMTP server */ }
}

class StubMailClient implements MailClient {
    public void send(String to, String body) {
        System.out.println("stub sent to " + to + ": " + body);
    }
}

class NotificationService {
    private final MailClient mailClient;

    NotificationService(MailClient mailClient) {
        this.mailClient = mailClient;
    }

    void notifyRideConfirmed(String rider) {
        mailClient.send(rider, "Your ride is confirmed");
    }
}

Passing the stub to NotificationService and confirming a ride for [email protected] prints:

text
stub sent to [email protected]: Your ride is confirmed
run in a container, javac 21

Compare this with a ride service that receives its repository through its constructor instead of creating it. In both, a constructor parameter replaces a new: one swaps a database, the other a mail server, and the code reads the same way each time. What they share is the shape: the class depends on an interface, and whoever creates the class chooses the implementation.

So when a class talks to anything outside the program, such as a database, a mail server or a payment service, expect it to receive that dependency through its constructor. Everything Spring does with beans assumes this shape.