의존성이란, 한 객체가 다른 객체를 어떤 방식으로든 참조한는 것을 말합니다.
class A {
var anotherClass: B
init(_ anotherClass: B){
self.anotherClass = anotherClass
}
func run(){
anothetClass.do()
}
}
위와 같이 A는 B라는 class가 없으면 실행이 안되고 말이 안됩니다. 즉 A는 B를 의존한다!
class BurgerChef {
var burgerRecipe: BurgerRecipe
init(burgerRecipe: BurgerRecipe){
self.burgerRecipe = burgerRecipe
}
func makeBurger() -> Burger {
print("Shout out \(burgerRecipe.chef.name)")
return burgerRecipe.cook()
}
}
이 식장은 햄버거를 만들기 전에 레시피를 만들어준 셰프의 이름을 외치고 버거를 만든다.
이 burgerchef는 BurgerRecipe 없이는 햄버거를 만들 수 없다.
즉 BurgerRecipe는 직접 의존성을 띄고 있다.
당연히 BurgerRecipe가 바뀌게 되면 BurgerChef는 수정해야한다.
하지만 BurgerRecipe.chef의 형태가 바뀌게 되도 바꿔야한다.
여기서 BurgerRecipe.chef은 간접 의존성을 띄고 있는 객체로 만약 chef의 형태를 바꿔도 BurgerRecipe는 바뀌지 않았는데, BurgerChef를 수정해야한다면 의존성이 전이된 상태가 된다.
즉 간접 의존성의 변경을 전이되지 않도록 설계해야한다
class BurgerChef {
var burgerRecipe: BurgerRecipe
init(burgerRecipe: BurgerRecipe){
self.burgerRecipe = burgerRecipe
}
func makeBurger() -> Burger {
burgerRecipe.shoutOut()
return burgerRecipe.cook()
}
}
protocol Recipe {
func shoutOut() -> Void
}
class BurgerRecipe: Recipe {
// ...
}
위와 같이 작성하게 되면 chef라는 것을 따로 두지 않고 간접 의존성의 변경이 전이 되지 않는다 .
class BurgerChef {
var burgerRecipe: BurgerRecipe
init(burgerRecipe: BurgerRecipe){
self.burgerRecipe = burgerRecipe
}
func makeBurger() -> Burger {
burgerRecipe.shoutOut()
return burgerRecipe.cook()
}
}
class BurgerChef {
var burgerRecipe = BurgerRecipe()
func makeBurger() -> Burger {
burgerRecipe.shoutOut()
return burgerRecipe.cook()
}
}
이름만 보고 뭐가 뭔지 알 수 있을 것이다.
아래는 반드시 BurgerRecipe를 의존하게 된다. 컴파일시에 저 instance를 생성한다는 것을 알게 된다.
여기서 우리는 의존성을 런타임 시점에 갖게 하는 것이 좋다!!!
왜냐하면
DI ( dependecy injection)은 의존성 주입? 을 확인해보자
전체적으로 수정하면
class Chef {
var recipe: Recipe
init(recipe: Recipe){
self.recipe = recipe
}
func make() -> Food {
recipe.cook()
}
}
protocol Recipe {
func cook() -> Food
}
class Chef {
var recipe: Recipe
init(recipe: Recipe){
self.recipe = recipe
}
func make() -> Server {
let server = Server()
return server.take(food: recipe.cook())
}
}
protocol Recipe {
func cook() -> Food
}
class Chef {
var recipe: Recipe
init(recipe: Recipe){
self.recipe = recipe
}
func make(server: Server) -> Server {
server.take(food: recipe.cook())
}
}
protocol Recipe {
func cook() -> Food
}
자 이제 서버를 정해서 요리를 보낼 것이다. 위와 아래의 차이는 뭘까?
관점을 손님에 입장에서 보자!
아래는 내가 서버를 정해서 주문한다면, 위는 그냥 아무 서버한테 음식을 전달하는 것이다.
자 이것은 어떤 문제를 가지고 올까?
만약 이 음식을 만드는 과정이 엄청 복잡하다고 생각해보자!
그래서 음식이 이 서버 저 서버한테 전달 전달 되고 다른 음식점 왔다가갔다가 한다.
그래도 내가 정한 서버가 나에게 올때 까지 기다리면 된다.
하지만 위라면 저게 내껀가? 할꺼다
즉 내부에서 의존하는 숨겨진 의존성을 갖게 되면 이것을 사용하는 입장에서 어떠한 사이드 이펙트가 발생할지 알 수 없다.