의존성이란? 무엇인가 ( 결합도 )

kio·2022년 11월 17일
0

CS

목록 보기
1/30
post-thumbnail

의존성이란?

의존성이란, 한 객체가 다른 객체를 어떤 방식으로든 참조한는 것을 말합니다.

class A {
	var anotherClass: B
	
	init(_ anotherClass: B){
			self.anotherClass = anotherClass
	}	

	func run(){
			anothetClass.do()	
	}
}

위와 같이 A는 B라는 class가 없으면 실행이 안되고 말이 안됩니다. 즉 A는 B를 의존한다!

직접 의존성 vs 간접 의존성

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라는 것을 따로 두지 않고 간접 의존성의 변경이 전이 되지 않는다 .

런타임 의존성 vs 컴파일 의존성

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를 생성한다는 것을 알게 된다.

여기서 우리는 의존성을 런타임 시점에 갖게 하는 것이 좋다!!!

왜냐하면

  1. 테스트하기 좋다.
    햄버거 맛이 없다면 레시피나 쉐프의 문제일 것이다. 레시피를 테스트하고 쉐프를 테스트하면 되지만 만약 위와 같다면 쉐프가 레시피를 가지고 있기 때문에 쉐프가 레시피 탓을 할 수도 있다.
  2. 재사용성이 높아진다.
    햄버거의 저장 프로퍼티가 있어 반드시 초기화 해야됬다면, BurgerChef는 반드시 그러한 햄버거만 만들 수 있는데, 외부에서 의존성을 주입하게 된다면 다양하게 대처도 할 수 있고, 여기에 BurgerRecipe가 아닌 Recipe protocol을 사용하면 더욱 재사용성이 높아진다.

DI ( dependecy injection)은 의존성 주입? 을 확인해보자

전체적으로 수정하면

class Chef {
	var recipe: Recipe

	init(recipe: Recipe){
		self.recipe = recipe
	}

	func make() -> Food {
		recipe.cook()
	}
}

protocol Recipe {
	func cook() -> Food 
}
  1. 뭐 업종변경을 할라면 해라! Recipe만 주면 다 만들어준다.
  2. 외부에서 Recipe를 주기 때문에 외부에서 주입하기 때문에 나의 정확성이 보장되면 다 레시피 탓!

명시적 의존성 vs 숨겨진 의존성

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 
}

자 이제 서버를 정해서 요리를 보낼 것이다. 위와 아래의 차이는 뭘까?

관점을 손님에 입장에서 보자!

아래는 내가 서버를 정해서 주문한다면, 위는 그냥 아무 서버한테 음식을 전달하는 것이다.

자 이것은 어떤 문제를 가지고 올까?

만약 이 음식을 만드는 과정이 엄청 복잡하다고 생각해보자!

그래서 음식이 이 서버 저 서버한테 전달 전달 되고 다른 음식점 왔다가갔다가 한다.

그래도 내가 정한 서버가 나에게 올때 까지 기다리면 된다.

하지만 위라면 저게 내껀가? 할꺼다

즉 내부에서 의존하는 숨겨진 의존성을 갖게 되면 이것을 사용하는 입장에서 어떠한 사이드 이펙트가 발생할지 알 수 없다.

0개의 댓글