In Part 1, I removed some of the static calls in my pizza program, leaving me with this code. It still has several static calls that interfere with testability:
public class PizzaServices {
private final Oven currentOven;
@Inject
public PizzaServices(Oven currentOven) {
this.currentOven = currentOven;
}
public Order createOrder(List<PizzaSpec> pizzas, Customer customer) {
Directions directions = Geography._getDirections_(
PizzaStore._getStoreAddress_(), customer.getDeliveryAddress());
if (directions == null || directions.getLengthInKm() > _MAX_DISTANCE_) {
throw new InvalidOrderException("Cannot deliver to , " +
customer.getDeliveryAddress());
}
int arrivalTime = _TIME_TO_PREPARE_
+ currentOven.schedule(_TIME_TO_PREPARE_, pizzas)
+ directions.estimateTravelTime();
Invoice invoice = Invoice._create_(pizzas, directions.getLengthInKm());
return new Order(pizzas, invoice, arrivalTime, customer, directions);
}
}
class PizzaModule extends AbstractModule {
protected void configure() {
requestStaticInjection(OrderPizzaAction.class);
requestStaticInjection(PizzaUtilities.class);
bind(Oven.class).toProvider(new Provider<oven>() {
public Oven get() {
return Oven.getCurrentOven();
}
});
}
}</oven>`</pre>
**Injecting @Named values**
Today I'll use injection and annotations to replace the static call to `PizzaStore.getStoreAddress()`. We could just bind the store address in the injector, but that could be confusing if there are multiple addresses in the program. The fix is to name this address with a binding annotation:<pre class="prettyprint">`public class PizzaServices {
private final Oven currentOven;
**private final Address storeAddress;**
@Inject
public PizzaServices(Oven currentOven,
**@Named("storeAddress") Address storeAddress**) {
this.currentOven = currentOven;
**this.storeAddress = storeAddress;**
}
public Order createOrder(List<PizzaSpec> pizzas, Customer customer) {
Directions directions = Geography._getDirections_(
**storeAddress**, customer.getDeliveryAddress());
...
}
}`</pre>And in the Module, we bind the named address using `annotatedWith`:<pre class="prettyprint">`class PizzaModule extends AbstractModule {
protected void configure() {
...
bind(Address.class)
**.annotatedWith(Names.named("storeAddress"))**
.toInstance(PizzaStore.getStoreAddress());
}
}`</pre>Now we can test the `PizzaService` interface without a dependency on static methods in the `PizzaStore` class.
**Removing Strings with custom annotations**
If the use of the String `"storeAddress"` upsets you, we can replace that name with a custom annotation. In the file `StoreAddress.java`, we create the annotation, which itself must be heavily annotated:<pre class="prettyprint">`@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.PARAMETER})
@BindingAnnotation
**public @interface StoreAddress {}**`</pre>Now we can replace the String name from the constructor with our annotation:<pre class="prettyprint">` @Inject
public PizzaServices(Oven currentOven,
**@StoreAddress** Address storeAddress) {
this.currentOven = currentOven;
this.storeAddress = storeAddress;
}`</pre>And update our module:<pre class="prettyprint">`class PizzaModule extends AbstractModule {
protected void configure() {
...
bind(Address.class)
**.annotatedWith(StoreAddress.class)**
.toInstance(PizzaStore.getStoreAddress());
}
}`</pre>
**Replacing a static method with an instance method**
Our `PizzaServices` class still has a big dependency on `Geography.java` for its `getDirections` method. Fortunately, we already know how to do this - just as we swapped `PizzaUtilities` with `PizzaServices` in [Part 1](http://publicobject.com/2007/07/guice-patterns-1-horrible-static-code.html), we can replace `Geography` with `GeographyServices` here. Then we can inject that into our `PizzaServices` class:<pre class="prettyprint">`public class PizzaServices {
private final Oven currentOven;
private final Address storeAddress;
**private final GeographyServices geographyServices;**
@Inject
public PizzaServices(Oven currentOven,
@StoreAddress Address storeAddress,
**GeographyServices geographyServices**) {
this.currentOven = currentOven;
this.storeAddress = storeAddress;
this.geographyServices = geographyServices;
}
public Order createOrder(List<PizzaSpec> pizzas, Customer customer) {
Directions directions = geographyServices.getDirections(
storeAddress, customer.getDeliveryAddress());
...
}
}
With the changes so far, we can create custom subclasses of Oven
and GeographyServices
to test createOrder
without dependencies. This means that our test will run faster and provide no false negatives.
The biggest benefit I get from non-static, injectable code is that if I need to make changes to the implementation of PizzaServices
, the edit-compile-execute cycle is dramatically faster.
In a future post, I'll improve this code by replacing the GeographyServices
and PizzaServices
classes with interfaces.