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


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.