[Flutter] Dart에서 Singleton pattern사용하기
Singleton pattern은 객체지향 프로그래밍에 있어서 아주 중요한 개념이다.
오늘은 Dart에서 Singleton 을 사용하는 방법에 대한 코드를 먼저 살펴보고 이를 하나 하나 뜯어보면서 분석해보고자 한다.
해당 글은 아래와 같은 순서로 진행된다.
- 싱글톤(Singleton) 패턴에 대한 개념
- Dart에서 싱클톤 패턴 만드는 방법
싱글톤(Singleton) 패턴에 대한 개념
싱글톤(Singleton) 패턴의 정의는 매우 간단하다.
객체의 인스턴스가 오직 하나만 생성되는 것, 즉 여러개의 생성자를 호출 하더라도 실제로 생성되는 인스턴스는 하나이다.
해당 부분을 코드로 살펴보자면 아래와 같다.
main() {
Singleton s1 = Singleton();
Singleton s2 = Singleton(); print(identical(s1, s2)); // true
print(s1 == s2); // true
}
싱글톤을 이용하게 되면 때에 따라 메모리 사용 측면에 있어서 효율성을 가져갈 수 있고 데이터간 공유가 쉽기 때문에 이를 위해 주로 이용된다.
하지만 위의 이유로 동시성 프로그래밍에 있어서 같은 데이터를 동시에 접근하게 되면 문제가 발생할 수 있으므로 설계시 이 점에 유의 해야 한다.
Dart에서 싱클톤 패턴 만드는 방법
우선 코드부터 살펴보자.
중요한 키워드 부터 정리하고 넘어가보자.
static
변수 또는 메서드가 인스턴스에 귀속되는 것이 아닌 클래스에 귀속되는 것
factory
factory의 경우 Dart에서 제공해주는 다소 특이한 키워드인데, 결국 생성자이다.
하지만 기존의 생성자와는 달리 반환값에 있어서 새로운 인스턴스의 생성이 가능하므로 좀 더 유연한 프로그래밍을 가능하게 한다.
위의 말은 같은 데이터 타입을 가지면 해당 인스턴스를 모두 return 할수 있다는 이야기이다.
예를 들면 factory가 선언된 클래스를 상속받은 자식 클래스의 인스턴스 또한 return이 가능하다.
...
// named generative
// delegates to the default generative constructor
Person.greek(String name) : this(name, "Greece");
// named factory
factory Person.greek(String name) {
return Greek(name);
}
}
class Greek extends Person {
Greek(String name) : super(name, "Greece");
}
해당 키워드를 이해하고 다시 한번 코드를 살펴보자.
여기서 아직 이해가 안가는 부분이 아래 두라인이라고 생각한다.
Singleton._privateConstructor();
static final Singleton _instance = Singleton._privateConstructor();
흔히 프로그래밍에서 이름(); 는 호출만을 의미하는 경우가 많기 때문에 혼동이 발생하게 되는 것이다.
결론부터 말하자면 Singleton._privateConstructor(); 이것은 호출이 아니라 생성자를 만드는 선언부이다.
빈생성자를 만드는 선언으로 이를 풀어서 표현하면 아래와 같다.
SingletonOne._privateConstructor() {
}
이를 약식으로 적은것이 Singleton._privateConstructor(); 이다.
참고로 클래스이름 뒤에 생성자 이름은 무엇이 와도 상관없다. 예를 들어 Singleton.someConstructor(); 이렇게 아무 이름이나 적어줘도 상관 없다.
그렇다면 왜 이렇게 굳이 빈 생성자를 생성하는 것일까?
Dart에선 생성자가 없을경우 자동으로 Public한 생성자를 만들어 버린다. 이를 막기위해 Private한 생성자를 만들어줘서 자동으로 만들어주는 생성자가 생성 되지 않도록 방지하는 것이다.
(Dart에서 언더바(‘_’) 를 붙이게 되면 Private을 의미한다.)
다음으로 static final Singleton _instance = Singleton._privateConstructor(); 에 대해 살펴보자.
이의 경우 앞의 경우와 달리 생성자를 호출하는 것으로 호출한 결과의 인스턴스를 _instance 변수에 할당한 것이다.
마지막으로 이를 다시 정리 해보자면 아래와 같다.
class Singleton {
// Private한 생성자 생성
Singleton._privateConstructor(); // 생성자를 호출하고 반환된 Singleton 인스턴스를 _instance 변수에 할당 static final Singleton _instance = Singleton._privateConstructor();// Singleton() 호출시에 _instance 변수를 반환
factory Singleton() {
return _instance;
}
}
결과적으로 Singleton(); 을 호출 할때 마다 factory에서 같은 인스턴스가 반환되므로 싱글톤 패턴이 만들어 진다.
참고 링크
이해하는데 도움주신 ‘Flutter 개발자 모임’의 ‘뉙’님에게 감사를 전합니다.