"의존성 주입은 25달러짜리 용어로 5센터짜리 개념이다." - 제임스 쇼어
Initializer Injection
, Setter Injection(Property Injection)
, Interface Injection
그리고 DI Container
이 있습니다. 의존성 주입 컨테이너(DI Container)
는 모든 의존성을 기록, 결정, 설정하는 책임을 가진 객체이며, 결정과 해결의 책임이란 DI 컨테이너가 생성자 인자와 객체 간의 관계를 알아야 한다는 의미입니다.
- 어떤 클래스 또는 모듈이 다른 클래스 또는 모듈에 의존할 때, 이를 의존성이라고 합니다.
- 의존성을 주입하는 역할을 하는 클래스 또는 모듈입니다. 이는 종종 DI 컨테이너라고 불리며, 의존성을 생성하고, 이를 필요로 하는 객체에 연결하는 역할을 합니다.
- 의존성이 주입되는 클래스 또는 모듈입니다. 클라이언트는 자신의 의존성을 직접 생성하지 않고, 주입자를 통해 받습니다.
- 객체가 자신의 의존성을 직접 생성하지 않고 외부에서 받기 때문에, 결합도가 낮아지고, 코드의 유연성이 향상됩니다.
- 의존성을 외부에서 주입받기 때문에, 테스트 시에 mock 객체와 같은 대체 의존성을 쉽게 주입할 수 있어 테스트가 용이해집니다.
- 의존성을 주입할 수 있기 때문에, 동일한 객체를 다양한 컨텍스트에서 재사용할 수 있고, 새로운 기능을 추가하거나 변경하기도 쉽습니다.
- 처음에 DI 패턴을 도입하면, 설정 및 관리할 부분이 많아져서 시스템의 복잡성이 증가할 수 있습니다.
- DI와 관련된 개념과 도구들을 익히는데 시간이 필요하며, 이해하지 못하고 사용하면 오히려 문제를 일으킬 수 있습니다.
protocol UserServiceProtocol {
func fetchUsers()
}
protocol RewardServiceProtocol {
func fetchRewards()
}
final class ViewModel {
private let userService: UserServiceProtocol
private let rewardService: RewardServiceProtocol
init(userService: UserServiceProtocol, rewardService: RewardServiceProtocol) {
self.userService = userService
self.rewardService = rewardService
}
}
protocol UserServiceProtocol {
func fetchUsers()
}
protocol RewardServiceProtocol {
func fetchRewards()
}
final class ViewModel {
private var userService: UserServiceProtocol?
private var rewardService: RewardServiceProtocol?
func setUserService(userService: UserServiceProtocol) {
self.userService = userService
}
func setRewardService(rewardService: RewardServiceProtocol) {
self.rewardService = rewardService
}
}
protocol UserServiceProtocol {
func fetchUsers()
}
protocol RewardServiceProtocol {
func fetchRewards()
}
protocol ServiceProtocol {
func users(service: UserServiceProtocol)
func rewards(service: RewardServiceProtocol)
}
final class ViewModel: ServiceProtocol {
private var userService: UserServiceProtocol?
private var rewardService: RewardServiceProtocol?
func users(service: UserServiceProtocol) {
self.userService = service
}
func rewards(service: RewardServiceProtocol) {
self.rewardService = service
}
}
protocol DIProtocol {
func register<Service>(type: Service.Type, service: Any)
func resolve<Service>(type: Service.Type) -> Service?
}
final class AppDIContainer: DIProtocol {
static let shared = AppDIContainer()
private init() { }
var services: [String: Any] = [:]
func register<Service>(type: Service.Type, service: Any) {
services["\(type)"] = service
}
func resolve<Service>(type: Service.Type) -> Service? {
return services["\(type)"] as? Service
}
}
services
는 모든 서비스를 보유하고 있는 String-Any 타입의 딕셔너리입니다. 딕셔너리의 String 키는 서비스의 이름을 나타내고, Any 값은 서비스 인스턴스를 참조합니다.protocol UserServiceProtocol {
func fetchUsers()
}
protocol RewardServiceProtocol {
func fetchRewards()
}
final class UserService: UserServiceProtocol {
func fetchUsers() {
print("User fetching")
}
}
final class RewardService: RewardServiceProtocol {
func fetchRewards() {
print("Reward fetching")
}
}
final class ViewModel {
private let userService: UserServiceProtocol
private let rewardService: RewardServiceProtocol
init(
userService: UserServiceProtocol = AppDIContainer.shared.resolve(type: UserServiceProtocol.self)!,
rewardService: RewardServiceProtocol = AppDIContainer.shared.resolve(type: RewardServiceProtocol.self)!
) {
self.userService = userService
self.rewardService = rewardService
}
func fetchUsers() {
userService.fetchUsers()
}
func fetchRewards() {
rewardService.fetchRewards()
}
}
let container = AppDIContainer.shared
container.register(type: UserServiceProtocol.self, service: UserService())
container.register(type: RewardServiceProtocol.self, service: RewardService())
let viewModel = ViewModel()
viewModel.fetchUsers()
viewModel.fetchRewards()
// User fetching
// Reward fetching
제가 학습한 내용을 요약하여 정리한 것입니다. 내용에 오류가 있을 수 있으며, 어떠한 피드백도 감사히 받겠습니다.
감사합니다.