Study/Dart,Flutter

21. [Dart] dart의 factory그리고 singleton에 대하여

코딩 잘 할거얌:) 2024. 1. 13. 17:32
반응형

언젠가 Flutter모임에 나가서 이런저런 이야기를 나누다가 어떤 분이 이러한 말씀을 해주셨다.

"dart의 factory에 대해서 한국어로 구글링하면 첫 페이지의 대부분은 전부 틀린 내용으로 작성이 되어있어요. 싱글톤에 대한 내용과 구분을 못해요."

그걸 듣고 "아 나라도 제대로 된 내용을 포스팅해보자!" 라는 생각에 포스팅을 하게 되었다.

여기가 원조에요!


 

Dart 키워드 factory란?

dart에서 factoy는 Design pattern에서의 factory pattern과 전혀 다른 것이다. design pattern의 factory는 아래의 링크를 참고하자.

https://refactoring.guru/ko/design-patterns/factory-method

 

팩토리 메서드 패턴

/ 디자인 패턴들 / 생성 패턴 팩토리 메서드 패턴 다음 이름으로도 불립니다: 가상 생성자, Factory Method 의도 팩토리 메서드는 부모 클래스에서 객체들을 생성할 수 있는 인터페이스를 제공하지

refactoring.guru

 

Dart에서 factory 키워드는 정확하게 생성자이다. dart dev에서도 표현이 "factory constructor"라고 소개하는 것을 보면 생성자 라고 할 수 있다.

 

코드를 보며 확인해보자.

class UserData {
  final String id;
  final String name;
  final String phone;

  UserData({required this.id, required this.name, required this.phone});

  factory UserData.emptyData() =>
      UserData(id: "빈칸입니다.", name: "빈칸입니다.", phone: "빈칸입니다.");
}

void main(List<String> arguments) {
  UserData userData = UserData.emptyData();
  print("userData id : ${userData.id}"); // 빈칸입니다.
  print("userData name : ${userData.name}"); // 빈칸입니다.
  print("userData phone : ${userData.phone}");// 빈칸입니다.
}

위의 코드를 확인해 보면, UserData라는 class에 factory 키워드로 UserData.emptyData()가 선언이 되어있다. 즉, 생성자로 class가 반환되는 것이랑 동일하다고 보면 된다.

Singleton이란?

https://refactoring.guru/ko/design-patterns/singleton

 

싱글턴 패턴

/ 디자인 패턴들 / 생성 패턴 싱글턴 패턴 다음 이름으로도 불립니다: Singleton 의도 싱글턴은 클래스에 인스턴스가 하나만 있도록 하면서 이 인스턴스에 대한 전역 접근​(액세스) 지점을 제공하

refactoring.guru

싱글턴 패턴은 클래스에 인스턴스가 하나만 있도록 하면서 이 인스턴스에 대한 전역 접근지점을 제공하는 생성 디자인 패턴이다.

... 누군가에겐 복잡한 이야기 일 수 있으니, 개발자들의 언어, 코드를 보면서 이해해 보도록 하자.

class Singleton {
  static final Singleton _singleton = Singleton._internal();

  factory Singleton() {
    return _singleton;
  }

  Singleton._internal();
}

class NotSingleton {}


var s1 = Singleton();
var s2 = Singleton();

var c1 = NotSingleton();
var c2 = NotSingleton();
print(identical(s1, s2)); // true
print(s1 == s2); // true

print(identical(c1, c2)); // false
print(c1 == c2); // false

 

위의 클래스를 제외하고 아래 s1과 s2를 보자. 분명 다르게 생성을 했는데, s1과 s2가 같다고 나온다.

그러나 c1 c2를 보게 되면 같지 않다고 나온다.

분명 선언되어 있는 클래스를 동일하게 생성하였는데, 두 개가 같고 다르게 나온다.

즉.

싱글턴 패턴은 클래스에 인스턴스가 하나만 있도록 하면서 이 인스턴스에 대한 전역 접근지점을 제공하는 생성 디자인 패턴이다.

를 간단하게 표현하자면, 선언된 class를 새롭게 생성하더라도 (s1, s2에서 서로 다르게 생성) 새롭게 생성된 class가 반환되는 것이 아닌, 기존에 생성되어 있던 클래스가 등장한다는 것이다.

그러면 "어떻게?? 이게 가능한 것일까?"라는 생각이 드는 분이 분명 있을 것이다.

싱글턴은 메모리에다가 값을 할당하기 때문에 이러한 일이 생기는 것이다. 물론 언어마다 프레임워크마다 다 다르기 때문에 메모리가 아닐 수 있지만, 어쨌거나 공통으로 사용하는 저장공간에다가 할당하여 사용하여 저장하는 것이다.

"어 그러면 전부 싱글턴으로 작성하면 좋은 거 아니에요?? 다른 곳에서도 바로 가져와서 쓸 수 있잖아요? 싱글턴 짱짱이네?"

싱글턴은 정말 써야 할 곳이 아니라면 지양하는 게 좋다. 왜냐하면 의존성관계 및 유닛테스트가 힘들기 때문이다. 

예를들어 기능 A와 B라는 곳에서 X라는 싱글턴을 이용한다고 가정해 보자.

기능 A라는 곳에서 싱글턴 X 생성, 수정하였다고 가정하면, A에서 사용된 싱글턴X가 기능 B에게 영향을 주게 된다. 서로 영향을 주지 않아야 하는 기능 A B에서 오류가 발생하는 것이다.

그러니 싱글턴은 사용해야 할 때만 사용하도록 하자.

factory와 singleton에 대하여

https://dart-ko.dev/language/constructors

 

생성자

Dart 생성자에 대한 모든 것.

dart-ko.dev

여기서 문제가 되는 점이, 공식문서 factory 설명에 따르면 

"항상 클래스의 새로운 인스턴스를 생성하지 않는 생성자를 구현하고 싶다면, factory 키워드를 사용하세요. 예를 들어, factory 생성자는 인스턴스를 캐시에서 반환하거나 서브타입의 인스턴스를 반환할 수 있습니다. Factory 생성자는 final 변수를 초기화 리스트에서 다루지 않는 로직을 사용하여 초기화하는 방법으로도 사용할 수 있습니다."

영어로는

"Use the factory keyword when implementing a constructor that doesn’t always create a new instance of its class. For example, a factory constructor might return an instance from a cache, or it might return an instance of a subtype. Another use case for factory constructors is initializing a final variable using logic that can’t be handled in the initializer list."

라고 되어있는데, 이 부분의 해석을 잘못 해석해서 factory가 singleton이라는 잘못된 이해가 발생하는 걸로 추정이 된다.

정확하게 설명하자면, dart에서 singleton기법을 사용하기 위해서 factory를 활용하라는 뜻이지, 생성자 factory가 singleton이라는 뜻은 아니다.

왜 아닌지 코드로 확인해 보도록 하자.

class UserData {
  final String id;
  final String name;
  final String phone;

  UserData({required this.id, required this.name, required this.phone});

  factory UserData.emptyData() =>
      UserData(id: "빈칸입니다.", name: "빈칸입니다.", phone: "빈칸입니다.");
}

void main(List<String> arguments) {
  UserData firstData = UserData.emptyData();
  UserData secondData = UserData.emptyData();
  print(firstData == secondData); // false
  print(identical(firstData, secondData)); // false
  print(firstData.hashCode == secondData.hashCode); // false
}

factory생성자가 singleton이라는 잘못된 해석을 하여 사용하면, firstData와 secondData가 같지 않다고 나오는 내용을 설명할 수 없다. 즉 factory생성자는 싱글턴으로 생성하는 것이 아니다.

싱글턴으로 작성한 팩토리 키워드가 아니라면 매번 새로운 인스턴스를 생성한다고 할 수 있다.

 

즉 정리하자면,

dart에서 Singleton은 factory 생성자를 활용한다.

dart factory 생성자는 는 singleton이 아니다.

dart factory 생성자를 활용하여 singleton을 만든다면, 매번 동일한 인스턴스를 반환한다.

로 정리할 수 있겠다.


틀리거나 잘못된 내용이 있다면 댓글로 자유롭게 지적 부탁드립니다.

728x90