다트에서 싱글 톤을 어떻게 만드나요?


205

싱글 톤 패턴은 클래스의 인스턴스가 하나만 생성되도록합니다. 다트에서 어떻게 만들 수 있습니까?


클래스 싱글 톤을 만드는 몇 가지 방법을 설명하는 몇 가지 답변을 아래에서 보았습니다. 그래서 나는 우리가 왜 class_name 객체를 좋아하지 않는지 생각하고 있습니다. if (object == null) return object = 새 클래스 _ 이름; else return object
— kumar를 종료하십시오

답변:


326

Dart의 팩토리 생성자 덕분에 싱글 톤을 쉽게 구축 할 수 있습니다.

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

  factory Singleton() {
    return _singleton;
  }

  Singleton._internal();
}

이렇게 구성 할 수 있습니다

main() {
  var s1 = Singleton();
  var s2 = Singleton();
  print(identical(s1, s2));  // true
  print(s1 == s2);           // true
}

2
두 번 인스턴스화하는 요점이 무엇입니까? 두 번째로 인스턴스화 할 때 오류가 발생하면 더 좋지 않습니까?
— westoque

53
나는 그것을 두 번 인스턴스화하지 않고 Singleton 객체에 대한 참조를 두 번 얻는다. 당신은 아마 실생활에서 두 번 연속으로하지 않을 것입니다 :) 예외가 발생하지 않기를 원합니다. "new Singleton ()"이라고 말할 때마다 동일한 싱글 톤 인스턴스가 필요합니다. 나는 약간 혼란 스럽다는 것을 인정한다. new여기서 "새로운 것을 구성"한다는 의미는 아니며, 단지 "생성자를 실행한다"고 말한다.
— 세스 래드

1
팩토리 키워드는 정확히 무엇입니까? 구현에 순전히 주석을 달고 있습니다. 왜 필요한가요?
— Καrτhικ

4
생성자를 사용하여 인스턴스를 얻는 것이 혼란 스럽습니다. new키워드는 클래스가하지 않은, 인스턴스화 것이 좋습니다. 정적 메서드를 사용 get()하거나 getInstance()Java에서와 같이 사용합니다.
— Steven Roose

11
@ SethLadd 이것은 매우 좋지만 몇 가지 설명이 필요합니다. Singleton._internal();실제로 생성자 정의 인 경우 메서드 호출처럼 보이는 이상한 구문 이 있습니다. 있습니다 _internal이름은. 그리고 다트가 일반적인 생성자를 사용하여 시작하고 (다트 아웃?) 필요한 경우 factory모든 발신자를 변경하지 않고 메소드로 변경할 수있는 멋진 언어 디자인 포인트가 있습니다.
— Jerry101

174

다음은 Dart에서 싱글 톤을 만드는 여러 가지 방법을 비교 한 것입니다.

1. 팩토리 생성자

class SingletonOne {

  SingletonOne._privateConstructor();

  static final SingletonOne _instance = SingletonOne._privateConstructor();

  factory SingletonOne() {
    return _instance;
  }

}

2. 게터가있는 정적 필드

class SingletonTwo {

  SingletonTwo._privateConstructor();

  static final SingletonTwo _instance = SingletonTwo._privateConstructor();

  static SingletonTwo get instance => _instance;
  
}

3. 정적 필드

class SingletonThree {

  SingletonThree._privateConstructor();

  static final SingletonThree instance = SingletonThree._privateConstructor();
  
}

설치하는 방법

위의 싱글 톤은 다음과 같이 인스턴스화됩니다.

SingletonOne one = SingletonOne();
SingletonTwo two = SingletonTwo.instance;
SingletonThree three = SingletonThree.instance;

노트 :

나는 원래 이것을 질문 으로 물었지만 위의 모든 방법이 유효하며 선택은 개인 취향에 달려 있다는 것을 발견했다.


3
방금 당신의 대답을 상향 조정했습니다. 허용 된 답변보다 훨씬 더 명확합니다. 한 가지 더 질문 : 두 번째 및 세 번째 방법의 경우 개인 생성자의 요점은 무엇입니까? 많은 사람들이 그렇게하는 것을 보았지만 요점을 이해하지 못합니다. 나는 항상 간단하게 사용 static final SingletonThree instance = SingletonThree()합니다. 같은 두 번째 방법으로 이동합니다 _instance. 개인 생성자를 사용하지 않는 것의 단점을 모르겠습니다. 지금까지 내 방식에 문제가 없습니다. 두 번째 및 세 번째 방법은 기본 생성자에 대한 호출을 차단하지 않습니다.
— sgon00

3
@ sgon00, 개인 생성자는 다른 인스턴스를 만들 수 없습니다. 그렇지 않으면 누구나 할 수 SingletonThree instance2 = SingletonThree()있습니다. 개인 생성자가있을 때 이렇게하면 다음과 같은 오류가 발생합니다.The class 'SingletonThree' doesn't have a default constructor.
— Suragch

41

나는 매우 직관적 인 독서를 찾지 못했습니다 new Singleton(). new실제로 새 인스턴스를 작성하지 않는 것을 알기 위해 문서를 읽어야합니다 .

싱글 톤을 수행하는 또 다른 방법이 있습니다 (기본적으로 Andrew가 위에서 말한 것).

lib / thing.dart

library thing;

final Thing thing = new Thing._private();

class Thing {
   Thing._private() { print('#2'); }
   foo() {
     print('#3');
   }
}

main.dart

import 'package:thing/thing.dart';

main() {
  print('#1');
  thing.foo();
}

Dart의 게으른 초기화로 인해 게터가 처음 호출 될 때까지 싱글 톤이 생성되지 않습니다.

원하는 경우 싱글 톤 클래스에서 싱글 톤을 정적 게터로 구현할 수도 있습니다. 즉 Thing.singleton, 최상위 게터 대신에.

또한 Bob Nystrom의 게임 프로그래밍 패턴 서적 에서 싱글 톤 에 대한 테이크를 읽어보십시오 .


1
Greg와 다트의 최상위 속성 기능 덕분에 이것은 더 의미가 있습니다.
— Eason PI

이것은 관용적이지 않습니다. 언어로 된 싱글 톤 패턴을 만드는 것은 꿈의 기능이며, 익숙하지 않기 때문에 버리는 것입니다.
— Arash

1
Seth의 예제와이 예제는 모두 싱글 톤 패턴입니다. "new Singleton ()"과 "singleton"구문에 대한 질문입니다. 후자가 더 명확하다는 것을 알았습니다. 다트의 팩토리 생성자는 유용하지만 이것이 좋은 유스 케이스라고 생각하지 않습니다. 또한 다트의 게으른 초기화가 잘 활용되지 않는 훌륭한 기능이라고 생각합니다. 또한 위의 Bob의 기사를 읽으십시오-대부분의 경우 싱글 톤에 대해 권장합니다.
— Greg Lowe

메일 링리스트에서이 글을 읽는 것이 좋습니다. groups.google.com/a/dartlang.org/d/msg/misc/9dFnchCT4kA/…
— 그렉 로우

이것은 더 좋습니다. "new"키워드는 새로운 객체의 구성을 의미합니다. 받아 들여진 해결책은 정말로 잘못 느낍니다.
— Sava B.

16

라이브러리 내에서 전역 변수를 사용하는 것은 어떻습니까?

single.dart:

library singleton;

var Singleton = new Impl();

class Impl {
  int i;
}

main.dart:

import 'single.dart';

void main() {
  var a = Singleton;
  var b = Singleton;
  a.i = 2;
  print(b.i);
}

아니면 눈살을 찌푸리게합니까?

글로벌 개념이 존재하지 않는 Java에서는 싱글 톤 패턴이 필요하지만 Dart에서 먼 길을 갈 필요는없는 것 같습니다.


5
최상위 변수는 멋지다. 그러나 single.dart를 가져올 수있는 사람은 누구나 "new Impl ()"을 자유롭게 구성 할 수 있습니다. 밑줄 생성자를 Impl에 줄 수는 있지만 싱글턴 라이브러리 내부 의 코드 는 해당 생성자를 호출 할 수 있습니다.
— 세스 래드

그리고 구현의 코드는 할 수 없습니까? 왜 최상위 변수보다 나은지 답을 설명 할 수 있습니까?
— Jan

2
안녕 @ 1 월, 그것은 나쁘거나 나쁘지 않습니다. 단지 다릅니다. Andrew의 예제에서 Impl은 싱글 톤 클래스가 아닙니다. 그는 인스턴스에 Singleton쉽게 액세스 할 수 있도록 최상위 변수를 올바르게 사용했습니다 . 위의 예제에서 Singleton클래스는 실제 싱글 톤이며 Singleton격리에 하나의 인스턴스 만 존재할 수 있습니다.
— Seth Ladd

1
세스, 당신이 옳지 않아요 선언 라이브러리 내부 에서 클래스의 인스턴스화 가능성을 제한 할 수있는 방법 이 없기 때문에 Dart에서는 진정한 싱글 톤을 구축 할 수있는 방법이 없습니다 . 항상 도서관 저자의 훈련이 필요합니다. 귀하의 예에서 선언 라이브러리는 원하는 횟수만큼 호출 하여 클래스 의 많은 오브젝트를 작성할 수 있습니다 . 는 IF 앤드류의 예에서 클래스 (개인했다 ), 그것은 당신의 예제와 같은 것입니다. 반면에 싱글 톤은 반 패턴이며 어쨌든 아무도 사용해서는 안됩니다. new Singleton._internal()SingletonImpl_Impl
— Ladicek

@Ladicek, 라이브러리 개발자가 new를 호출하지 않도록 믿지 마십시오 Singelton._internal(). singelton 클래스의 개발자는 클래스를 여러 번 설치할 수도 있다고 주장 할 수 있습니다. 물론 enum singelton이 있지만 나에게는 이론적으로 사용됩니다. 열거 형은 싱글 톤이 아닌 열거 형입니다 ... 최상위 변수 사용 (@Andrew 및 @Seth) : 아무도 최상위 변수에 쓸 수 없습니까? 그것은 결코 보호되지 않거나 무언가를 놓치고 있습니까?
— Tobias Ritzau

12

다른 가능한 방법은 다음과 같습니다.

void main() {
  var s1 = Singleton.instance;
  s1.somedata = 123;
  var s2 = Singleton.instance;
  print(s2.somedata); // 123
  print(identical(s1, s2));  // true
  print(s1 == s2); // true
  //var s3 = new Singleton(); //produces a warning re missing default constructor and breaks on execution
}

class Singleton {
  static final Singleton _singleton = new Singleton._internal();
  Singleton._internal();
  static Singleton get instance => _singleton;
  var somedata;
}

11

const 생성자 및 팩토리로 다트 싱글 톤

class Singleton {
  factory Singleton() =>
    const Singleton._internal_();
  const Singleton._internal_();
}


void main() {
  print(new Singleton() == new Singleton());
  print(identical(new Singleton() , new Singleton()));
}

5

인스턴스 후에 객체를 변경할 수없는 싱글 톤

class User {
  final int age;
  final String name;

  User({
    this.name,
    this.age
    });

  static User _instance;

  static User getInstance({name, age}) {
     if(_instance == null) {
       _instance = User(name: name, idade: age);
       return _instance;
     }
    return _instance;
  }
}

  print(User.getInstance(name: "baidu", age: 24).age); //24

  print(User.getInstance(name: "baidu 2").name); // is not changed //baidu

  print(User.getInstance()); // {name: "baidu": age 24}

4

Swift 스타일의 싱글 톤을 선호하는 사람을 위해 @Seth Ladd 답변을 수정했습니다 .shared.

class Auth {
  // singleton
  static final Auth _singleton = Auth._internal();
  factory Auth() => _singleton;
  Auth._internal();
  static Auth get shared => _singleton;

  // variables
  String username;
  String password;
}

견본:

Auth.shared.username = 'abc';

4

모든 대안을 읽은 후에 나는 이것을 생각해 냈습니다.

class AccountService {
  static final _instance = AccountService._internal();

  AccountService._internal();

  static AccountService getInstance() {
    return _instance;
  }
}

3
나는 바꿀 것 getInstance에서 방법 instance: 다음과 같은 특성static AccountService get instance => _instance;
— gianlucaparadise

나는 이것을 좋아한다. 인스턴스가 반환되고 다른 방법이 사용되기 전에 몇 가지를 추가하고 싶습니다.
— chitgoks

4

간단한 답변은 다음과 같습니다.

class Singleton {
  static Singleton _instance;

  Singleton._();

  static Singleton get getInstance => _instance = _instance ?? Singleton._();
}

3

다음은 다른 솔루션을 결합한 간결한 예입니다. 싱글 톤에 액세스하려면 다음을 수행하십시오.

  • 사용하여 singleton인스턴스를 가리키는 전역 변수
  • 일반 Singleton.instance 패턴.
  • 인스턴스를 반환하는 팩토리 인 기본 생성자를 사용합니다.

참고 : 싱글 톤을 사용하는 코드가 일관되도록 세 가지 옵션 중 하나만 구현해야합니다.

Singleton get singleton => Singleton.instance;
ComplexSingleton get complexSingleton => ComplexSingleton._instance;

class Singleton {
  static final Singleton instance = Singleton._private();
  Singleton._private();
  factory Singleton() => instance;
}

class ComplexSingleton {
  static ComplexSingleton _instance;
  static ComplexSingleton get instance => _instance;
  static void init(arg) => _instance ??= ComplexSingleton._init(arg);

  final property;
  ComplexSingleton._init(this.property);
  factory ComplexSingleton() => _instance;
}

복잡한 초기화를 수행해야하는 경우 나중에 프로그램에서 인스턴스를 사용하기 전에 수행해야합니다.

예

void main() {
  print(identical(singleton, Singleton.instance));        // true
  print(identical(singleton, Singleton()));               // true
  print(complexSingleton == null);                        // true
  ComplexSingleton.init(0); 
  print(complexSingleton == null);                        // false
  print(identical(complexSingleton, ComplexSingleton())); // true
}

1

안녕하세요 이런 식은 어때요? 매우 간단한 구현 인젝터 자체는 싱글 톤이며 클래스도 추가되었습니다. 물론 매우 쉽게 확장 할 수 있습니다. 더 정교한 것을 찾고 있다면이 패키지를 확인하십시오 : https://pub.dartlang.org/packages/flutter_simple_dependency_injection

void main() {  
  Injector injector = Injector();
  injector.add(() => Person('Filip'));
  injector.add(() => City('New York'));

  Person person =  injector.get<Person>(); 
  City city =  injector.get<City>();

  print(person.name);
  print(city.name);
}

class Person {
  String name;

  Person(this.name);
}

class City {
  String name;

  City(this.name);
}


typedef T CreateInstanceFn<T>();

class Injector {
  static final Injector _singleton =  Injector._internal();
  final _factories = Map<String, dynamic>();

  factory Injector() {
    return _singleton;
  }

  Injector._internal();

  String _generateKey<T>(T type) {
    return '${type.toString()}_instance';
  }

  void add<T>(CreateInstanceFn<T> createInstance) {
    final typeKey = _generateKey(T);
    _factories[typeKey] = createInstance();
  }

  T get<T>() {
    final typeKey = _generateKey(T);
    T instance = _factories[typeKey];
    if (instance == null) {
      print('Cannot find instance for type $typeKey');
    }

    return instance;
  }
}

0

이 작동합니다.

class GlobalStore {
    static GlobalStore _instance;
    static GlobalStore get instance {
       if(_instance == null)
           _instance = new GlobalStore()._();
       return _instance;
    }

    _(){

    }
    factory GlobalStore()=> instance;


}

후속 질문을 답변으로 게시하지 마십시오. 이 코드의 문제점은 약간 장황하다는 것입니다. static GlobalStore get instance => _instance ??= new GlobalStore._();할것이다. 무엇입니까 _(){}어떻게해야? 중복되는 것 같습니다.
— Günter Zöchbauer

죄송합니다. 후속 질문이 아닌 제안이었습니다. _ () {}은 개인 생성자를 만들 것입니다.
— Vilsad PP

생성자는 클래스 이름으로 시작합니다. 이것은 리턴 유형이 지정되지 않은 일반적인 개인용 인스턴스 메소드입니다.
— Günter Zöchbauer 8

1
공감에 대해 유감스럽게 생각하지만 품질이 좋지 않다고 생각하고 기존 답변 외에도 가치를 추가하지 않습니다.
— Günter Zöchbauer

2
이 코드는 질문에 대답 할 수 있지만 문제를 해결하는 방법 및 / 또는 이유에 대한 추가 컨텍스트를 제공하면 답변의 장기적인 가치가 향상됩니다.
— Karl Richter

0

new싱글 톤에서 호출하는 것과 같은 키워드 또는 다른 생성자 를 사용하는 것을 좋아하지 않기 때문에 inst예를 들어 정적 게터를 사용하는 것이 좋습니다 .

// the singleton class
class Dao {
    // singleton boilerplate
        Dao._internal() {}
        static final Dao _singleton = new Dao._internal();
        static get inst => _singleton;

    // business logic
        void greet() => print("Hello from singleton");
}

사용법 예 :

Dao.inst.greet();       // call a method

// Dao x = new Dao();   // compiler error: Method not found: 'Dao'

// verify that there only exists one and only one instance
assert(identical(Dao.inst, Dao.inst));
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.