서로 다른 객체 사이에서 의존 관계가 있다는 것을 말한다. 즉, 의존하는 객체가 수정되면 다른 객체도 영향을 받는 것.
struct Eat {
func coffee() { print("Coffee") }
func meal() { print("Pizza") }
}
struct Person {
var todayEat: Eat
func coffee() { todayEat.coffee() }
func meal() { todayEat.meal() }
}
-> Person 객체는 Eat 객체를 인스턴스로 사용하고 있으므로 Eat 객체에 의존성이 생긴다. 만약, 이때 Eat 객체에 중요한 수정이나 오류가 발생한다면 Person 객체도 영향을 받을 수 있다. 의존성을 가지는 코드가 많아진다면, 재활용성도 저하되고 매번 의존성을 가지는 객체들을 함께 수정해 주어야 함.
이러한 의존성 문제 해결을 위해 나온 개념이 의존성 주입(DI: Dependency Injeciton)이다.
외부에서 객체를 생성해서 넣는 것이다.
classs Eat: Menu {
var coffee: String
var meal: String
init(coffee: String, meal: String) {
self.coffee = coffee
self.meal = meal
}
func printCoffee() { print("Coffee") }
func printMeal() { print("Pizza") }
}
let menu = Eat(coffee: "Coffee", meal: "Pizza")
이렇게 생성자 (등등..) 를 활용하여 외부에서 주입이 가능!
그렇다면... 의존성 주입을 왜 할까?
(1) 유닛 테스트가 용이해진다.
(2) 코드의 재활용성이 높아진다.
(3) 객체 간의 종속성(의존성)이 낮아진다.
(4) 객체 간의 결합도를 낮추면서 유연한 코드를 작성할 수 있다.
protocol Menu {
func printCoffee()
func printMeal()
}
class Eat: Menu { // Menu 프로토콜 채택 후
var coffee: String
var meal: String
init(coffee: String, meal: String) {
self.coffee = coffee
self.meal = meal
}
func printCoffee() { print("커피") }
func printMeal() { print("피자") } // Menu 프로토콜에서 정의한 함수를 실체화시킴
}
// 이제부터가 중요합니다...
struct Person {
var todayEat: Menu // todayEat 변수는 추상적인 객체인 Menu 타입에 의존하게 됨.
func printCoffee() { todayEat.printCoffee() }
func printMeal() { todayEat.printMeal() }
mutating func changeMenu(menu: Menu) { // changeMenu 함수를 활용하여 의존성 주입 가능
self.todayEat = menu
}
}
이렇게 구현한다면 Eat 객체와 Person 객체는 거의 독립적인 객체가 된다. Eat 객체를 수정하거나 Person 객체를 수정한다고 해서 상대 객체를 함께 수정해야 하는 문제를 방지할 수 있다.
let menu = Eat(Coffee: "아메리카노", meal: "피자")
let anotherMenu = Eat(Coffee: "라떼", meal: "햄버거")
var personTest = Person(todayEat: menu)
personTest.printCoffee() // 아메리카노
personTest.chanfeMenu(menu: anotherMenu)
personTest.printCoffee() // 라떼