A클래스에서 B클래스가 필요한 경우 A클래스가 B클래스에 의존한다고 하며 B클래스가 A클래스의 dependency가 됩니다. Dependency injection이란 클래스에 필요한 dependency를 클래스 내부에서 생성하는 것이 아니라 외부에서 주입을 받는 것을 말합니다.
Decreased Coupling
클래스간의 coupling을 줄일 수 있습니다. 하지만 이는 dependency injection으로만 이루어지는 것이 아니라 dependency를 concrete type이 아니라 abstract type으로 받아야 합니다.
Reduced Boilerplate Code
외부에서 dependency를 주입받기 때문에 내부에서 생성할 필요가 없어집니다. 그래서 dependency를 생성하기 위한 boilerplate code가 감소합니다.
Concurrent Development
클래스에서 abstract한 type으로 dependency를 주입받을 경우, 서로 알아야 하는 것은 dependency의 abstract type이므로 해당 클래스의 개발과 dependency 클래스의 개발을 동시에 할 수 있습니다.
Testable
Dependency를 쉽게 교체할 수 있으므로 test double instance를 클래스에 넘겨주어 test할 수 있습니다.
Increased Reusability
이점 또한 dependency를 쉽게 교체할 수 있으므로 클래스가 필요한 곳에서 dependency를 생성하여 주입해주기만 하면 사용이 가능합니다.
클래스의 constructor에 필요한 dependency들을 정의하여 생성시 주입받도록 하는 방식입니다. 위의 그림은 constructor injection을 이용하여 클래스를 생성하는 sequence diagram으로 필요한 dependency인 ProduceService를 생성하여 HomeController의 constructor에 주입하는 것을 볼 수 있습니다. 기본적으로 constructor injection을 사용하는 것이 좋습니다.
Dependency를 클래스의 property에 주입하는 방식입니다. 보통 property를 private하게 하고 setter를 public하게 하여 주입받으므로 setter injection이라고도 불립니다. Property injection은 dependency의 좋은 기본 구현체가 존재하고 다른 구현체를 주입하는 것이 optional할 경우 혹은 클래스의 생성이 개발자에 의해 이루어지지 않을 때 사용해야합니다. Android의 경우 Activity, Fragment 등은 android framework에 의해 생성되므로 property injection을 사용합니다.
Method injection은 method의 parameter로 method에 필요한 dependency를 주입하는 방식입니다. Method call을 할 때마다 dependency가 달라지는 경우 method injection을 사용합니다.
Dependency는 기준에 따라 stable dependency와 volatile dependency로 나눌 수 있습니다.
아래 조건들을 만족하는 경우 stable dependency라고 합니다.
이미 존재하는 클래스 혹은 모듈
새로운 버전에서 큰 변화가 없다고 예상되는 경우
결정적인 알고리즘(deterministic algorithm)을 가진 타입
다른 클래스 혹은 모듈로 교체, decorate, intercept되지 않다고 예상되는 경우
Interception의 경우 interceptor 클래스를 생성하여 수행할 경우 stable하다고 분류될 수도 있습니다.
아래 조건 중 하나라도 만족하면 volatile dependency입니다.
Runtime Environment를 구성하는 dependency
Dependency가 아직 존재하지 않고 개발중인 경우
Dependency가 모든 machine에 설치될 수 없는 경우
비용이 많이 드는 third-party library 혹은 모든 OS에서 사용이 가능하지 않은 dependency가 이에 속합니다.
Dependency가 비결정적 알고리즘(nondeterministic algorithm)을 가진 경우
이 경우는 특히 unit testing에서 중요합니다. Unit test에서 모든 test들은 deterministic해야 하지만 nondeterministic algorithm을 가진 dependency를 교체할 수 없으면 test를 deterministic하게 할 수 없기 때문입니다. Nondeterministic algorithm은 보통 난수, 시간에 의존하는 알고리즘 등이 있습니다.
Volatile dependency의 coupling을 abstract type을 도입하여 줄이고 dependency injection을 사용하여야 합니다.
[1] "Dependency injection," Wikipedia, last modified n.d., accessed Jun 8, 2022, https://en.wikipedia.org/wiki/Dependency_injection.
[2] Mark Seemann and Steven van Deursen, Dependency Injection Principles, Practices, and Patterns (n.p.: Manning Publications, 2019), 26-27, 83-120.