Card 02/ 05

ExampleDifficulty: Beginner1 min

Dependency Injection Example: A Repository Passed to the Constructor

No framework at all: a constructor parameter is the whole technique.

Dependency Injection needs no framework. Rewriting RideService so that its repository arrives as a constructor parameter is the entire technique, and a test can then pass in a fake repository that needs no database.

First the types: an interface that says what a repository can do, the real implementation, and a fake one for tests. RideService now asks for any RideRepository in its constructor:

java
interface RideRepository {
    Ride findById(String id);
}

class PostgresRideRepository implements RideRepository {
    public Ride findById(String id) { /* talks to Postgres */ return null; }
}

class FakeRideRepository implements RideRepository {
    public Ride findById(String id) { return new Ride(id); }
}

class RideService {
    private final RideRepository repository;

    RideService(RideRepository repository) {
        this.repository = repository;
    }

    Ride getRide(String id) {
        return repository.findById(id);
    }
}

A test, or any other caller, chooses which implementation to pass in:

java
RideService service = new RideService(new FakeRideRepository());
Ride ride = service.getRide("ride-1");
System.out.println("Got ride " + ride.id + " with no database running");
text
Got ride ride-1 with no database running
run in a container, javac 21

The fake returns a ride without touching a database, and RideService cannot tell the difference, because it only knows the interface.

So new RideService(new FakeRideRepository()) is Dependency Injection, written by hand. Spring's job, once it is involved, is to do the same handing-in automatically for every bean (an object Spring creates and manages for you), based on what each constructor asks for.