1. Singleton Pattern
상황을 가정해봅시다.
만약에 뷰컨을 넘어가면서 솝트 인적사항을 얻어야하는 경우를 생각해봅시다
A ViewController에서는 이름을, B ViewController에서는 파트를, C ViewController에서는 시작기수를 받아야 된다고 생각해보자고요! 그러면 일반적으로 가장 먼저 드는 생각은 화면 이동시 해당 객체를 넘겨주는 겁니다! 한번 구현해볼까요?
struct SoptMember {
var name: String?
var part: String?
var start: Int?
}
class AViewController: UIViewController {
var soptMember = SoptMember(name: "희재", part: nil, start: nil)
//대충 화면 이동 코드, dataBind
func pushToBVC() {
let bVC = BViewController()
bVC.dataBind(soptMember: soptMember)
self.navigationController?.pushViewController(bVC, animated: true)
}
}
class BViewController: UIViewController {
var soptMember: SoptMember?
func dataBind(soptMember: SoptMember) {
self.soptMember = soptMember
setPart()
}
func setPart() {
self.soptMember?.part = "아요"
}
func pushToCVC() {
let cVC = BViewController()
cVC.dataBind(soptMember: soptMember ?? SoptMember())
self.navigationController?.pushViewController(cVC, animated: true)
}
}
class CViewController: UIViewController {
var soptMember: SoptMember?
func dataBind(soptMember: SoptMember) {
self.soptMember = soptMember
setStart()
}
func setStart() {
self.soptMember?.start = 31
}
}
해당 코드에서 느껴지는 불편함
어차피 한개만 있는 인스턴스의 프로퍼티를 꾸며주는 건데, 왜? 이곳저곳에서 불러야 되는거지? 계속 dataBind를 “굳이” 해야되나? 라는 부분일것입니다!
즉 우리가 하고 싶은건, 다양한 ViewController에서 쉽게 접근할 수 있도록 만드는 것이겠죠! 그것을 충족시켜줄 수 있는 디자인패턴이 바로 싱글톤 패턴입니다!
2. 싱글톤이란?
한 개의 클래스로 만드는 객체는 단 한개여야만 한다는 규칙을 가진 디자인 패턴
프로그램 전체에 단 하나의 전역 객체를 만들어놓고 여기저기서 이 하나의 객체에만 접근할 수 있도록 하면 된다.
즉, 특정 용도로 객체를 하나만 생성하여, 공용으로 사용하고 싶을 때 사용하는 디자인 유형이다!
클래스에 대한 인스턴스는 최초 생성될 때, 한번만 생성해서 전역을 두고,그 이후로는 생성된 인스턴스에만 접근 가능하도록 하는 디자인
2-1. 싱글톤 구현하기
- static 프로퍼티로 Instance 생성하기
→ 전역에서 사용될 것이기 때문에 static을 Instance를 저장할 프로퍼티를 생성해준다. - init 함수 접근제어자를 private로 지정하기
→ init 함수를 호출해 또 다른 Instance를 생성하는 것을 막기 위해, init() 함수 접근 제어자를 private로 지정한다. - static 프로퍼티로 싱글톤 클래스 접근하기
→ 어느 클래스에서든 shared란 static 프로퍼티로 접근하면, 하나의 Instance를 공유하는 것이다.
class SoptMember {
static let shared = SoptMember() // 싱글톤 객체 생성
private init() {} // 생성자 접근 제어
var name: String?
var part: String?
var start: Int?
}
class AViewController: UIViewController {
let soptMember = SoptMember.shared()
soptMember.name = "류희재"
}
2-2. 싱글톤 패턴의 장단점
장점
1. 유일하게 존재하는 인스턴스로의 접근을 통제
싱글톤 클래스 자체가 인스턴스를 캡슐화하기 때문에, 다른 클래스 입장에선 어떻게 이 인스턴스에 접근할 수 있는지를 제어한다!
2. 메모리를 단 한번만 사용한다.
해당 객체는 단 한번만 만들어지기 때문에 메모리 관리가 편하다는 장점이 있다. 한번 만들어둔 객체를 꾸준히 재사용할 수 있다.
3. 객체 접근 시간이 줄어든다.
다시 메모리를 할당하고, 초기화(init)하는 과정과 시간이 줄어드니, 한번 만들어두기만 하면 다시 접근할 때는 매번 객체를 만들 때보다 조금이나마 더 빠르다.
4. 전역 범위에서 상태, 데이터 전달이 쉬워진다.
어디서든 접근할 수 있기 때문에 어떤 상태를 표시하거나, 데이터를 공유하기에 적절하다. 단 하나의 객체만 있고, 잘 신경써서 동시성 문제가 생기지 않도록 짜기만 한다면 걱정 없이 상태나 데이터를 전달할 수 있다.
단점
1. 테스트가 힘들다.
싱글톤 class는 위에서 살짝 봤던 것처럼 init을 private로 설정해 유일성을 보장하는 경우가 많다. 이렇게 생성을 제한하면 테스트용 객체를 만들기가 불편해진다.
2. 의존성을 만들어낸다.
‘여기저기’서, ‘쉽게’ 접근할 수 있는 탓에, 프로젝트 이곳저곳에서 상상 이상으로 사용될 수 있다. 무분별한 사용 → 어떤 객체와 연결되어있는지 찾기 불편한 구조 싱글톤에 많은 기능을 맡길 경우 → SRP를 위반하게 되겠죠!
가장 큰 단점은 편의성 때문에 투명성을 희생한다
싱글톤 패턴은 전역 범위에서 어디서든 접근할 수 있다는 강력한 장점이 있지만, 프로젝트의 규모가 점점 커지다보면 싱글톤 패턴에 접근하는 객체를 추적할 수 없게 된다. 이러한 싱글톤 패턴의 단점을 보완할 수 있는 방법은 의존성 주입이다. 의존성 주입을 활용하면 클래스가 생성되기 위한 필요한 객체를 명확히 확인하며 투명성을 회복할 수 있다. (의존성은 나중에 다른 글로 또 다룰 예정이다)
3. 언제 사용하면 좋을까?
공통된 객체를 여러개 생성해서 사용해야 하는 곳에서 많이 사용됨
⇒ 지속적으로 수정 및 조회하는 요청이 많은 객체를 싱글톤 설계하면 용이함!
ex) 공통된 네트워크 작업 세션, 앱 전체를 관통하는 설정 정보, 로그 기록 객체
4. 한줄평
송지훈 선생님의 말씀을 인용해보겠습니다!
💡 싱글톤 객체는 만들고 사용하기에 매우 간단한 형태라 손쉽게 사용할 수 있고 모든 클래스에서 전역적인 접근이 가능하기에, 쓰기/ 읽기가 많이 일어나는 부분에 대해 용이하게 사용할 수 있다
but. 자칫 싱글톤을 오남용하면 싱글톤이 주는 장점보다 단점이 더 크기에 멀티 스레드 문제 / 의존성 / 테스트 코드 작성 등 여러가지를 고려해서 ”안전한” 싱글톤을 설계할 필요가 있다!
결국 싱글톤 패턴은 편리함이 아닌, 어떤 시점에도 클래스의 인스턴스가 단 한개만 존재하도록 보장하는 것이 더 중요하다는 것을 인지하고 제한적으로 사용될 때 더 가치있게 활용할 수 있다는 것을 깨달아야 한다!
'🔗 Architecture & Design Pattern' 카테고리의 다른 글
| [🍔 Design Pattern] 햄버거로 배우는 디자인 패턴 (2) Factory Pattern (1) | 2024.09.04 |
|---|---|
| [Design Pattern] Why Design Pattern? (0) | 2024.09.02 |