Card 04/ 05
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.
class FareCalculator {
private final LiveSurgePricingClient pricingClient = new LiveSurgePricingClient();
double fareFor(String zone, double baseFare) {
return baseFare * pricingClient.surgeMultiplierFor(zone);
}
}See a fix
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.
← → move through sections and cards