디자인패턴이란 뭘까요?
프로그램 설계를 할 때 많이 발생했던 문제점을
객체 관계를 활용해서 해결할 수 있도록
하나의 "규약"처럼 정해둔 패턴을 말해요!
iOS 개발을 공부하고 있는 지금으로서는 Delegate 패턴이 가장 친숙하네요~ :D
하지만 오늘은 싱글톤에 대해 공부해봤습니다ㅎㅎ
🩵 싱글톤 패턴
특정한 객체의 인스턴스가 오직 1개만 생성된다고 정해둔 패턴
1개의 인스턴스를 프로젝트 전역에서 사용할 수 있게 하는 패턴이에요
이미 생성된 인스턴스를 가져다가 쓰기 때문에 인스턴스를 또 생성하지 않고 바로 프로퍼티, 메서드를 사용할 수 있어요!
이렇게 1회만 생성되고 전역으로 사용할 수 있기 때문에
데이터 공유가 쉽고 메모리를 절약하는데 도움이 되어요
실제 코드로는 이런 정도의 영향을 가지고 있다고 해볼게요
하지만 피치못할 사정으로 해당 클래스에 문제가 생긴다면 어떨까요?
좀 극단적으로 프로퍼티를 삭제해봤습니다ㅎㅎ
어떤가요, 3개가 전부 맛이 가버렸죠?
그럼 이렇게도 해 볼까요?
중간에 값을 바꿔치기 해보는 거에요
중간에 갑자기 값이 변해버렸죠?
글로벌 상태로 참조되어 관리중이기 때문에 중간에 값을 바꾸어버리면 전역적으로 상태가 변경됩니다
이렇듯 싱글톤 인스턴스에 아주 작은 부분만 영향을 받고 있었더라도 문제는 피할 수 없을 겁니다
이걸 의존도가 높다, 결합도가 높다, 각 객체가 독립적이지 않다. 라고 표현하는데요
결국 해당하는 객체들이 독립적이지 않다는 것은 유닛테스트가 어렵다는 의미로 이어지고
Testable 하지 않다! 라는 결론까지 도달하게 되네요!
(최근 마이붐인 유닛 테스트 관련..)
🩵 DI
Dependency Injection | 의존성 주입
이런 단점을 보완하려면 의존성 주입이라는 걸 하면 된다고 하는데
의존성 주입이 뭘까요?
의존성 주입이란,
객체가 필요로 하는 타 객체를 외부에서 생성하고 주입해서 사용하는 소프트웨어 설계 패턴입니다
쉽게 말하면 필요한 것을 직접 만들지 않고, 외부에서 만들어 넣어주는 방식이라고 보면 돼요
어느 한 식당이 있습니다
이 가게의 요리사는 파스타 메뉴를 추가하고 싶었지만 파스타 레시피를 몰라요
이런 요리사가 직접 레시피를 만드는 것은 실수할 가능성도 있고 굉장히 번거로운 작업이 될 거에요
하지만 가게 사장이 어딘가에서 딱 맞는 레시피를 공수해 왔다고 해 봅시다
그러면 요리사는 해당 레시피만 전달 받아서 가장 잘 하는 요리만 해도 되지 않을까요?
이런걸 바로 의존성 주입이라고 합니다!
필요한 걸 직접 만들지 않고, 외부에서 만든 다음 전달해주는 방식이에요
이런 상황이라면 더 와닿을까요..?
같은 데이터를, 여러 객체에다가, 외부에서 전달하는 걸 표현해본 그림입니다 ... ;)
🩵 기본적인 싱글톤 패턴 구현하기
static 키워드 사용하여 단일 인스턴스 만들기
class Chef {
static let shared = Chef()
private init() {}
}
외부에서 다시 인스턴스를 생성하는 걸 방지하기 위해 private으로 init 구현까지 해 줄게요
간단하게 싱글톤 기본 골조는 끝입니다!
여기서는 요리사가 직접 레시피도 만들고 조리도 해야해요
class Chef {
static let shared = Chef()
private init() {}
func createPastaRecipe() -> String {
return "요리사가 만든 스파게티 레시피"
}
func cookPasta() -> String {
let recipe = createPastaRecipe()
return "직접 만든 레시피[\(recipe)]로 스파게티 요리!"
}
}
여기까지 오면 진짜 싱글톤은 끝입니다
🩵 만든 싱글톤 패턴에 의존성 주입하기
싱글톤 패턴의 의존성 주입은 보통 프로토콜을 이용하여 진행돼요
사장이 레시피를 전달해 주는 모양새로 만들거에요
전달받는 부분을 프로토콜로 만들어봅시다
protocol RecipeProviding {
func fetchPastaRecipe() -> String
}
class Manager: RecipeProviding {
func fetchPastaRecipe() -> String {
return "사장이 건네준 스파게티 레시피~"
}
}
프로토콜을 구현하고 해당 프로토콜을 준수하는 클래스 하나를 만들었어요
책임을 분리시켜주는 겁니다!
class Chef {
static let shared = Chef()
private init() {}
func cookPasta(using recipeProvider: RecipeProviding) -> String {
let recipe = recipeProvider.fetchPastaRecipe()
return "건네받은 레시피[\(recipe)]로 스파게티 요리!"
}
}
Chef 라는 싱글톤도 Recipe 프로토콜에 의존하게 되었어요
이 정도면 레시피 제작과 요리하는 행위의 결합이 잘 해제된 것 같죠?
이렇게 결합도가 낮아지는 걸 영어로는 디커플링이라고 한대요!
의존도가 낮은 싱글톤은 유지보수가 더 쉽고 유연하게 대처할 수 있습니다
레시피가 추가될 때는 RecipeProviding을 준수하는 새로운 구현체를 만들면 되니까요 >_<
🩵 iOS 에서 싱글톤을 사용하는 경우
let screen = UIScreen.main
let userDefault = UserDefaults.standard
let application = UIApplication.shared
let fileManager = FileManager.default
let notification = NotificationCenter.default
뭐 요런 기능에서 싱글톤 패턴을 채택하고 있다고 하네요!
저 개인적으로는 유저 로그인/로그아웃/정보 같은 사용자 세션 정보나 네트워크 관리 하는데 싱글톤을 써 본 적이 있습니다ㅎ
책을 보다가... 싱글톤 패턴에 대해 좀 파고들어 봤네요
대체 왜 디자인 패턴은 보면 볼수록 헷갈리는지?
static 키워드, class 키워드 같은 것도 알아보긴 했는데...ㅎㅎ
그 부분은 게시글을 분리해서 쓰는걸로!
(23.03.19 추가)
이어지는 게시글!
https://yy-dev.tistory.com/160
그림출처 : 본인
https://developer.apple.com/documentation/swift/managing-a-shared-resource-using-a-singleton
https://jeong-pro.tistory.com/86
https://velog.io/@naroti/iOS-%EA%B0%9C%EB%B0%9C-Singleton-Pattern-q4k3uzgf0n
https://medium.com/@ranga.c222/singleton-vs-dependency-injection-in-ios-swift-47a056a822f
https://babbab2.tistory.com/66