Introduction to Dependency Injection with a real life example

This example is an introduction to the Dependency Injection concept. It is based on the PHP library PHP-DI.

Classic implementation

Given you have:

class GoogleMapsService {
    public function getCoordinatesFromAddress($address) {
        // calls Google Maps webservice
    }
}

class OpenStreetMapService {
    public function getCoordinatesFromAddress($address) {
        // calls OpenStreetMap webservice
    }
}

The classic way of doing things is:

class StoreService {
    public function getStoreCoordinates($store) {
        $geolocationService = new GoogleMapsService();
        // or $geolocationService = GoogleMapsService::getInstance() if you use singletons
        return $geolocationService->getCoordinatesFromAddress($store->getAddress());
    }
}

Now we want to use the OpenStreetMapService instead of GoogleMapsService, how do we do? We have to change the code of StoreService, and all the other classes that use GoogleMapsService.

Without dependency injection, your classes are tightly coupled with their dependencies.

Dependency injection implementation

The StoreService now uses dependency injection:

class StoreService {
    /**
     * @Inject
     * @var GeolocationService
     */
    private $geolocationService;

    public function getStoreCoordinates($store) {
        return $this->geolocationService->getCoordinatesFromAddress($store->getAddress());
    }
}

And the services are defined using an interface:

interface GeolocationService {
    public function getCoordinatesFromAddress($address);
}

class GoogleMapsService implements GeolocationService {
    public function getCoordinatesFromAddress($address) {
        // calls Google Maps webservice
    }
}

class OpenStreetMapService implements GeolocationService {
    public function getCoordinatesFromAddress($address) {
        // calls OpenStreetMap webservice
    }
}

If you use PHP-DI (a PHP dependency injection library), you then configure which implementation will be used:

$container->set('GeolocationService')
          ->bindTo('OpenStreetMapService');

If you change your mind, there’s just one line of configuration to change.