programing

TypeScript에서 Singleton을 정의하는 방법

lastmemo 2023. 2. 20. 23:52
반응형

TypeScript에서 Singleton을 정의하는 방법

TypeScript에서 클래스에 싱글톤 패턴을 구현하는 가장 편리한 방법은 무엇입니까? (느린 초기화 여부와 관계없이)

TS 2.0 이후로는 컨스트럭터에서 가시성 수식자를 정의할 수 있게 되어 다른 언어에서 익숙한 것처럼 TypeScript에서도 싱글톤을 실행할 수 있게 되었습니다.

예:

class MyClass
{
    private static _instance: MyClass;

    private constructor()
    {
        //...
    }

    public static get Instance()
    {
        // Do you need arguments? Make it a regular static method instead.
        return this._instance || (this._instance = new this());
    }
}

const myClassInstance = MyClass.Instance;

@Drenai 님은 컴파일된 raw javascript를 사용하여 코드를 작성하면 TS의 제약이 없어지고 컨스트럭터가 숨겨지지 않기 때문에 여러 인스턴스화를 방지할 수 없다고 지적해 주셔서 감사합니다.

TypeScript의 싱글톤 클래스는 일반적으로 안티 패턴입니다.대신 네임스페이스를 사용할 수 있습니다.

쓸모없는 싱글톤 패턴

class Singleton {
    /* ... lots of singleton logic ... */
    public someMethod() { ... }
}

// Using
var x = Singleton.getInstance();
x.someMethod();

네임스페이스 등가물

export namespace Singleton {
    export function someMethod() { ... }
}
// Usage
import { SingletonInstance } from "path/to/Singleton";

SingletonInstance.someMethod();
var x = SingletonInstance; // If you need to alias it for some reason

가장 좋은 방법은 다음과 같습니다.

class SingletonClass {

    private static _instance:SingletonClass = new SingletonClass();

    private _score:number = 0;

    constructor() {
        if(SingletonClass._instance){
            throw new Error("Error: Instantiation failed: Use SingletonClass.getInstance() instead of new.");
        }
        SingletonClass._instance = this;
    }

    public static getInstance():SingletonClass
    {
        return SingletonClass._instance;
    }

    public setScore(value:number):void
    {
        this._score = value;
    }

    public getScore():number
    {
        return this._score;
    }

    public addPoints(value:number):void
    {
        this._score += value;
    }

    public removePoints(value:number):void
    {
        this._score -= value;
    }

}

사용 방법은 다음과 같습니다.

var scoreManager = SingletonClass.getInstance();
scoreManager.setScore(10);
scoreManager.addPoints(1);
scoreManager.removePoints(2);
console.log( scoreManager.getScore() );

https://codebelt.github.io/blog/typescript/typescript-singleton-pattern/

다음 접근방식은 기존 클래스처럼 쉽게 사용할 수 있는 싱글톤 클래스를 만듭니다.

class Singleton {
    private static instance: Singleton;
    //Assign "new Singleton()" here to avoid lazy initialisation

    constructor() {
        if (Singleton.instance) {
            return Singleton.instance;
        }

        this. member = 0;
        Singleton.instance = this;
    }

    member: number;
}

★★new Singleton()작업을 수행하면 동일한 인스턴스가 반환됩니다.다만, 이것은 유저가 예측할 수 없는 일이 있습니다.

다음 예시는 사용자에게 보다 투과적이지만 다른 사용법이 필요합니다.

class Singleton {
    private static instance: Singleton;
    //Assign "new Singleton()" here to avoid lazy initialisation

    constructor() {
        if (Singleton.instance) {
            throw new Error("Error - use Singleton.getInstance()");
        }
        this.member = 0;
    }

    static getInstance(): Singleton {
        Singleton.instance = Singleton.instance || new Singleton();
        return Singleton.instance;
    }

    member: number;
}

: 사법 usage :var obj = Singleton.getInstance();

사실 매우 단순해 보이는 다음과 같은 패턴이 보이지 않는 것에 놀랐습니다.

// shout.ts
class ShoutSingleton {
  helloWorld() { return 'hi'; }
}

export let Shout = new ShoutSingleton();

사용.

import { Shout } from './shout';
Shout.helloWorld();

다음 6줄을 임의의 클래스에 추가하여 "싱글톤"으로 만듭니다.

class MySingleton
{
    private constructor(){ /* ... */}
    private static _instance: MySingleton;
    public static getInstance(): MySingleton
    {
        return this._instance || (this._instance = new this());
    };
}

테스트 예:

var test = MySingleton.getInstance(); // will create the first instance
var test2 = MySingleton.getInstance(); // will return the first instance
alert(test === test2); // true

[편집] : 메서드가 아닌 속성을 통해 인스턴스를 가져오려면 Alex answer를 사용합니다.

이를 위한 나의 솔루션:

export default class Singleton {
    private static _instance: Singleton = new Singleton();

    constructor() {
        if (Singleton._instance)
            throw new Error("Use Singleton.instance");
        Singleton._instance = this;
    }

    static get instance() {
        return Singleton._instance;
    }
}

2021년 갱신

이제 생성자는 개인일 수 있습니다.

export default class Singleton {
    private static _instance?: Singleton;

    private constructor() {
        if (Singleton._instance)
            throw new Error("Use Singleton.instance instead of new.");
        Singleton._instance = this;
    }

    static get instance() {
        return Singleton._instance ?? (Singleton._instance = new Singleton());
    }
}

TS Playground에서의 테스트

이것에 클래스식을 사용할 수 있습니다(1.6부터라고 생각합니다).

var x = new (class {
    /* ... lots of singleton logic ... */
    public someMethod() { ... }
})();

또는 클래스가 내부적으로 유형에 액세스해야 하는 경우 이름을 사용합니다.

var x = new (class Singleton {
    /* ... lots of singleton logic ... */
    public someMethod(): Singleton { ... }
})();

또 다른 옵션은 일부 정적 멤버를 사용하여 싱글톤 내에서 로컬클래스를 사용하는 것입니다.

class Singleton {

    private static _instance;
    public static get instance() {

        class InternalSingleton {
            someMethod() { }

            //more singleton logic
        }

        if(!Singleton._instance) {
            Singleton._instance = new InternalSingleton();
        }

        return <InternalSingleton>Singleton._instance;
    }
}

var x = Singleton.instance;
x.someMethod();

내 생각에 아마 범용 제품을 butter로 사용할 것 같다.

class Singleton<T>{
    public static Instance<T>(c: {new(): T; }) : T{
        if (this._instance == null){
            this._instance = new c();
        }
        return this._instance;
    }

    private static _instance = null;
}

사용법

순서 1

class MapManager extends Singleton<MapManager>{
     //do something
     public init():void{ //do }
}

순서 2

    MapManager.Instance(MapManager).init();

Object 함수를 사용할 수도 있습니다.프리즈().심플하고 간단합니다.

class Singleton {

  instance: any = null;
  data: any = {} // store data in here

  constructor() {
    if (!this.instance) {
      this.instance = this;
    }
    return this.instance
  }
}

const singleton: Singleton = new Singleton();
Object.freeze(singleton);

export default singleton;

전혀 했습니다. Typescript를 가 없기 에 더 합니다.또한 이 버전을 호출할 필요가 없기 때문에getInstance()항상 메서드를 사용합니다.

import express, { Application } from 'express';

export class Singleton {
  // Define your props here
  private _express: Application = express();
  private static _instance: Singleton;

  constructor() {
    if (Singleton._instance) {
      return Singleton._instance;
    }

    // You don't have an instance, so continue

    // Remember, to set the _instance property
    Singleton._instance = this;
  }
}

이것에는 다른 단점이 있습니다.의 '' ''가Singleton에는 속성이 없습니다.타이프 스크립트. ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★_express생성자에서 나중에 할당하더라도 값을 사용하여 초기화하지 않는 한 Typescript는 정의되지 않은 것으로 간주하기 때문입니다.이것은 strict 모드를 디세블로 하는 것으로 해결할 수 있습니다만, 가능하면 수정하지 않는 것이 좋습니다.이 메서드에는 또 다른 단점도 있습니다.컨스트럭터가 실제로 호출될 때마다 기술적으로 다른 인스턴스가 생성되지만 액세스할 수 없기 때문입니다.이론적으로는 메모리 누설이 발생할 수 있습니다.

/**
 * The Singleton class defines the `getInstance` method that lets clients access
 * the unique singleton instance.
 */
class Singleton {
    private static instance: Singleton;

    /**
     * The Singleton's constructor should always be private to prevent direct
     * construction calls with the `new` operator.
     */
    private constructor() { }

    /**
     * The static method that controls the access to the singleton instance.
     *
     * This implementation let you subclass the Singleton class while keeping
     * just one instance of each subclass around.
     */
    public static getInstance(): Singleton {
        if (!Singleton.instance) {
            Singleton.instance = new Singleton();
        }

        return Singleton.instance;
    }

    /**
     * Finally, any singleton should define some business logic, which can be
     * executed on its instance.
     */
    public someBusinessLogic() {
        // ...
    }
}

/**
 * The client code.
 */
function clientCode() {
    const s1 = Singleton.getInstance();
    const s2 = Singleton.getInstance();

    if (s1 === s2) {
        console.log('Singleton works, both variables contain the same instance.');
    } else {
        console.log('Singleton failed, variables contain different instances.');
    }
}

clientCode();

이것은 아마도 타이프 스크립트로 싱글톤을 만드는 데 가장 긴 과정일 것입니다만, 큰 어플리케이션에서는 더 잘 작동합니다.

먼저 ".utils/Singleton.ts"와 같은 싱글톤 클래스가 필요합니다.

module utils {
    export class Singleton {
        private _initialized: boolean;

        private _setSingleton(): void {
            if (this._initialized) throw Error('Singleton is already initialized.');
            this._initialized = true;
        }

        get setSingleton() { return this._setSingleton; }
    }
}

다음으로 라우터 싱글톤 ".navigation/Router.ts"가 필요하다고 가정합니다.

/// <reference path="../utils/Singleton.ts" />

module navigation {
    class RouterClass extends utils.Singleton {
        // NOTICE RouterClass extends from utils.Singleton
        // and that it isn't exportable.

        private _init(): void {
            // This method will be your "construtor" now,
            // to avoid double initialization, don't forget
            // the parent class setSingleton method!.
            this.setSingleton();

            // Initialization stuff.
        }

        // Expose _init method.
        get init { return this.init; }
    }

    // THIS IS IT!! Export a new RouterClass, that no
    // one can instantiate ever again!.
    export var Router: RouterClass = new RouterClass();
}

좋습니다.필요한 장소에서 초기화 또는 Import를 할 수 있습니다.

/// <reference path="./navigation/Router.ts" />

import router = navigation.Router;

router.init();
router.init(); // Throws error!.

싱글톤을 이렇게 하면 좋은 점은 타이프스크립트 클래스의 장점을 모두 사용할 수 있다는 것입니다.또한 싱글톤 로직은 어느 정도 떨어져 있고 필요에 따라 쉽게 제거할 수 있습니다.

타이프스크립트를new instance()할 수 가져온 생성자가 없는 정적 클래스도 동일하게 작동할 수 있습니다.

고려사항:

export class YourSingleton {

   public static foo:bar;

   public static initialise(_initVars:any):void {
     YourSingleton.foo = _initvars.foo;
   }

   public static doThing():bar {
     return YourSingleton.foo
   }
}

Import를 할 수 .YourSingleton.doThing()그런데 때문에 저는 보통 생성자를 사용합니다.intialise()Import:

import {YourSingleton} from 'singleton.ts';

YourSingleton.initialise(params);
let _result:bar = YourSingleton.doThing();

에 스태틱해야 this할 수 있습니다.YourSingleton.

위의 모든 옵션을 사용하여 이 스레드를 청소하고 플레이한 후 적절한 컨스트럭터로 만들 수 있는 싱글톤을 선택했습니다.

export default class Singleton {
  private static _instance: Singleton

  public static get instance(): Singleton {
    return Singleton._instance
  }

  constructor(...args: string[]) {
    // Initial setup

    Singleton._instance = this
  }

  work() { /* example */ }

}

셋업이 합니다( 「초기 셋업」(「중간 셋업」)이 필요하게 됩니다.main.ts , 「」index.ts는, 할 수 .
new Singleton(/* PARAMS */)

코드 내 하세요.Singleton.instnace ;를 하려면 ,workSingleton.instance.work()

다음과 같은 클래식한 패턴을 구현한 후

class Singleton {
  private instance: Singleton;
  
  private constructor() {}

  public getInstance() {
    if (!this.instance) { 
      this.instance = new Singleton();
    }
    return this.instance;
  }
}

다른 수업도 독신자가 되고 싶을 때 꽤 쓸모없다는 걸 깨달았어요.연장할 수 없어요.독신자가 되고 싶은 모든 수업 시간에 독신자 같은 걸 써야 해

구조를 위한 장식가들.

@singleton
class MyClassThatIsSingletonToo {}

데코레이터는 직접 쓰셔도 되고, npm부터 가져가셔도 됩니다.@keenondrums/singleton 패키지의 이 프록시 기반 구현은 충분히 깔끔하다는 것을 알았습니다.

IFFE를 사용하여 보다 일반적인 javascript 접근 방식을 사용하는 또 다른 방법이 있습니다.

module App.Counter {
    export var Instance = (() => {
        var i = 0;
        return {
            increment: (): void => {
                i++;
            },
            getCount: (): number => {
                return i;
            }
        }
    })();
}

module App {
    export function countStuff() {
        App.Counter.Instance.increment();
        App.Counter.Instance.increment();
        alert(App.Counter.Instance.getCount());
    }
}

App.countStuff();

데모 표시

또 다른 옵션은 모듈에서 기호를 사용하는 것입니다.이 방법으로 클래스를 보호할 수 있습니다.또한 API의 최종 사용자가 일반 Javascript를 사용하고 있는 경우:

let _instance = Symbol();
export default class Singleton {

    constructor(singletonToken) {
        if (singletonToken !== _instance) {
            throw new Error("Cannot instantiate directly.");
        }
        //Init your class
    }

    static get instance() {
        return this[_instance] || (this[_instance] = new Singleton(_singleton))
    }

    public myMethod():string {
        return "foo";
    }
}

사용방법:

var str:string = Singleton.instance.myFoo();

사용자가 컴파일된 API js 파일을 사용하는 경우 사용자가 클래스를 수동으로 인스턴스화하려고 할 경우에도 오류가 발생합니다.

// PLAIN JAVASCRIPT: 
var instance = new Singleton(); //Error the argument singletonToken !== _instance symbol

순수한 싱글톤이 아니라 (초기화가 게으르지 않을 수 있음)의 도움을 받아 같은 패턴입니다.namespaces.

namespace MyClass
{
    class _MyClass
    {
    ...
    }
    export const instance: _MyClass = new _MyClass();
}

Singleton 객체에 대한 액세스:

MyClass.instance

이것이 가장 간단한 방법이다.

class YourSingletoneClass {
  private static instance: YourSingletoneClass;

  private constructor(public ifYouHaveAnyParams: string) {

  }
  static getInstance() {
    if(!YourSingletoneClass.instance) {
      YourSingletoneClass.instance = new YourSingletoneClass('If you have any params');
    }
    return YourSingletoneClass.instance;
  }
}

예를 들어 클라이언트의 접속을 작성할 수 있는 싱글톤 클래스를 만들고 그 접속된 클라이언트를 어디에서나 사용할 수 있도록 하겠습니다.

import nats, { Stan } from 'node-nats-streaming';

class NatsWrapper {
  private _client?: Stan;

  get client() {
    if (!this._client) {
      throw new Error('Cannot access NATS client before connecting');
    }
    return this._client;
  }

  connect(clusterId: string, clientId: string, url: string) {
    this._client = nats.connect(clusterId, clientId, { url });

    return new Promise((resolve, reject) => {
      this.client.on('connect', (result) => {
        console.log('Connected to NATS');
        resolve(result);
      });
      this.client.on('error', (err) => {
        reject(err);
      });
    });
  }
}

// since we create and export the instace, it will act like a singleton
export const natsWrapper = new NatsWrapper();

여기서 먼저 index.ts/app.ts 파일에 conneciton을 생성하면 아무 곳이나 Import하는 것만으로 동일한 클라이언트에 액세스할 수 있습니다.

인덱스.ts

    await natsWrapper.connect(
      'ticketing',
      'client_id_random_str',
      'http://nats-srv:4222'
    );

일부 파일.ts

import { natsWrapper } from '../nats-wrapper';

const abc = () =>{
    console.log(natsWrapper.client)
}

나는 타이프스크립트에서 싱글톤 패턴 클래스를 선언하기 위한 적절한 해결책을 찾기 위해 애를 먹었다.

저는 아래가 더 실용적인 해결책이라고 생각합니다.

class MySingletonClass {
    public now:Date = new Date();
    public arg:string;
    constructor(arg:string) {
        this.arg = arg;

        // Make singleton
        if ('instance' in MySingletonClass) return Object.getOwnPropertyDescriptor(MySingletonClass, 'instance')?.value;
        Object.assign(MySingletonClass, { instance: this });
    }
}

const a = new MySingletonClass('a');
console.log(a);

const b = new MySingletonClass('b');
console.log(b);

console.log('a === b', a === b);
console.log('a.now === b.now', a.now === b.now);
class Singleton<CREATOR extends () => any>
{
  private m_fnGetInstance: () => ReturnType<CREATOR>;
  public get Instance() { return this.m_fnGetInstance(); }
  public constructor(creator: CREATOR) {
    this.m_fnGetInstance = () => {
      const instance = creator();
      this.m_fnGetInstance = () => instance;
      return instance;
    };
  }
}

const Foo = new Singleton(() => new class {
  public bar() {}
});

Foo.Instance.bar();

조건부 분기가 없기 때문에 고속입니다.

놀이터.

깃헙

namespace MySingleton {
  interface IMySingleton {
      doSomething(): void;
  }
  class MySingleton implements IMySingleton {
      private usePrivate() { }
      doSomething() {
          this.usePrivate();
      }
  }
  export var Instance: IMySingleton = new MySingleton();
}

이렇게 하면 Ryan Cavanaugh가 수용한 답변과 달리 인터페이스를 적용할 수 있습니다.

언급URL : https://stackoverflow.com/questions/30174078/how-to-define-singleton-in-typescript

반응형