Card 04/ 05

ExerciseDifficulty: Beginner1 min

Practice: Rewriting a Class to Use Constructor Injection

A fare calculator that builds its own pricing client, and the change that makes it testable.

Constructor injection means a class receives its dependencies as constructor parameters instead of creating them. The class below creates its own dependency; your task is to rewrite it so the dependency is handed in.

FareCalculator builds its own pricing client, which calls a live pricing service to get the surge multiplier (the factor a busy zone's fares are multiplied by). Rewrite it so the client arrives through the constructor, then decide how a test would supply a client that always returns 1.4.

java
class FareCalculator {
    private final LiveSurgePricingClient pricingClient = new LiveSurgePricingClient();

    double fareFor(String zone, double baseFare) {
        return baseFare * pricingClient.surgeMultiplierFor(zone);
    }
}
See a fix
java
interface PricingClient {
    double surgeMultiplierFor(String zone);
}

class LiveSurgePricingClient implements PricingClient {
    public double surgeMultiplierFor(String zone) { /* calls a real pricing service */ return 1.0; }
}

class FareCalculator {
    private final PricingClient pricingClient;

    FareCalculator(PricingClient pricingClient) {
        this.pricingClient = pricingClient;
    }

    double fareFor(String zone, double baseFare) {
        return baseFare * pricingClient.surgeMultiplierFor(zone);
    }
}

An interface, PricingClient, describes what the calculator needs, and the constructor accepts any implementation of it. Because the interface has a single method, a test can pass a lambda (a short inline function): new FareCalculator(zone -> 1.4). Then fareFor("downtown", 10.0) returns 14.0, with no pricing service running anywhere.