Dependency Injection (Java)

Context: FIT2099_MOC · the technique that implements DIP · a class is given its collaborators instead of new-ing them · enables swapping and testing Problem it solves: a class that hard-codes new ConcreteService() — hand the service in from outside so the class depends only on the abstraction.

Quick Revision

  • 🎯 Trigger: a class constructs its own dependency with new (even a concrete subclass of an interface) ➔ inject the dependency from an external injector.
  • ⚡ Key Constraint: DIP alone still leaves the client creating the concrete service (this.mt = new MoodleMessager()) — DI removes even that by having an outside class supply it.

🔧 Minimal Working Example

// WITHOUT DI: Car constructs its own dependency — locked to PetrolEngine
class Car {
    private Engine engine = new PetrolEngine();   // hard-wired
    public void start() { engine.start(); }
}
 
// WITH DI (constructor injection): the engine is supplied
class Car {
    private final Engine engine;                  // abstraction
    public Car(Engine engine) { this.engine = engine; }   // injected
    public void start() { engine.start(); }
}
class MyApp {                                     // the INJECTOR
    public static void main(String[] args) {
        Engine engine = new PetrolEngine();       // chosen here, not inside Car
        Car car = new Car(engine);
        car.start();
    }
}

Expected output: Car works with any Engine; swapping to ElectricEngine needs no edit to Car.

  • Terminologyclient (uses the interface, e.g. Car) · service (injected concrete, PetrolEngine) · interface (Engine) · injector (external class that supplies the service).
  • DI vs DIP ➔ DIP = depend on abstractions; DI = the abstraction is handed in by someone else, so the client doesn’t even know the concrete type.
  • Benefits ➔ reusability, easy refactoring, and (biggest) testability.

🔀 Variations — three injection types

TypeMechanismTrade-off
Constructorservice passed to the constructorinjector must be whoever builds the client; can be final
Setterinjector calls a public setX(service)swap service on a running client; needs a public setter (weakens hiding)
Interfaceclient implements InjectX with injectX(service)like setter, but you name the method / aren’t limited to one
  • Connascence of execution ➔ with setter/interface injection the object isn’t usable until setX() is called — an ordering dependency to watch.

🏛️ Structure

classDiagram
  class Engine { <<interface>> +start() void }
  class PetrolEngine { +start() void }
  class Car { +Car(Engine e) +start() void }
  class MyApp { +main() void }
  Engine <|.. PetrolEngine
  Car --> Engine : uses
  MyApp ..> Car : creates & injects
  MyApp ..> PetrolEngine : creates

✍️ Practice

⚠️ Common Mistakes

  • 💡 DI ≠ DIP ➔ DIP is the principle (depend on abstractions); DI is the technique (an injector supplies them) — a class can obey DIP yet still new its service (not DI).
  • 💡 Setter injection leaks state ➔ a public setter needed for injection also lets any caller mutate the service — a mild information-hiding cost.
  • 💡 Frameworks do this for you ➔ Spring / Guice supply the injector (and manage persistence, REST, web wiring) — everywhere in industry, beyond this unit’s scope.