“직접”
생성해서 쓰는 것이 아니라,"외부에서"
생성해서 사용하는 객체에 넣어주는 것PetOwner
클래스 안에서 Dog
의 인스턴스를 “직접” 생성해서 사용하고 있다.class PetOwner {
let dog = Dog()
func walkWithPet() {
dog.walk()
}
}
class Dog {
func walk() {
print("개가 걷는다")
}
}
let mason = PetOwner()
mason.walkWithPet()
개, 고양이에 대한 경우의 수를 따로 만들어야 하므로
기존의 PetOwner 를 DogOwner 로 변경하고,
Cat 을 인스턴스 화 해서 가지는 CatOwner 를 따로 만들어줬다😭
class DogOwner {
let dog = Dog()
func walkWithPet() {
dog.walk()
}
}
class CatOwner {
let cat = Cat()
func walkWithPet() {
cat.walk()
}
}
class Cat {
func walk() {
print("고양이가 걷는다")
}
}
let paul = CatOwner()
paul.walkWithPet()
protocol Pet {
func walk()
}
class Parrot: Pet {
func walk() {
print("패럿이 걷는다")
}
}
class PetOwner {
let pet: Pet
// 💉✨의존성주입!✨💉
init(pet: Pet) {
self.pet = pet
}
func walkWithPet() {
pet.walk()
}
}
let parrot = Parrot()
let logan = PetOwner(pet: parrot)
logan.walkWithPet()
여러 타입의 동물들을 묶은 Pet 이라는 프로토콜을 정의하고,
PetOwner를 초기화 할 때 “외부에서 생성된” Pet 타입을 받아서 넣어줬다!
어떤 타입의 동물이라도 해당 Pet 을 채택하고 있다면 PetOwner 내부에서 walk 라는 역할을 수행할 수 있게 된다!
💡유지보수 측면에서 굉장히 설계가 유연해지고 재사용성이 높아질 수 있다!
(테스트가 용이하다는 말이 있던데…!)
(나도 아직 테스트 코드를 제대로 짜본 적은 없지만) 위 동물산책 프로젝트에서 예시를 들어보자면!
처음 짰던 Dog 를 직접 내부에서 생성하는 PetOwner 라면 PetOwner를 테스트를 할 때마다 Dog 의 동작 로직에 대해서도 신경을 써야 하지만,
protocol Pet 을 의존성 주입으로 받는 PetOwner 라면 테스트 할 때 유니콘, 불사조.. 같은 목업객체의 펫을 임의로 넣어줘서 테스트를 진행할 수 있다!
Dependency Inversion Principle
! (말이 참 어려워 보인다)위의 프로젝트를 예시로 들면, 구체적인 객체인 PetOwner
클래스는 추상화한 Pet
프로토콜에 의존해야 한다는 것!
그렇게 함으로서, PetOwner 객체와 Dog, Cat… 객체는 사실상 독립적인 객체가 되는 것!
양쪽 중에 한 쪽을 수정해야 하더라도 다른 쪽의 객체도 수정해야 하는 문제를 방지할 수 있음!
😲 나의 느낌으로는, 객체의 독립성을 유지시킬 수 있게 프로토콜이라는 추상화된 징검다리를 놓아준 느낌… 으로 받아들여진다!