Сделать декоратор для класса Singelton - машинопись - PullRequest
4 голосов
/ 04 мая 2020

Я строю проект, используя VueJS с Typescript. Я чувствую себя комфортно, используя сервисы вместо любой библиотеки управления состоянием, такой как Vuex. Но при написании сервисов я всегда должен копировать и вставлять некоторый код в каждый класс сервисов, чтобы сделать его отдельным:

class MyService {
  private static Instance: MyService;
  public static getInstance() {
    if (!MyService.Instance) {
      MyService.Instance = new MyService();
    }
    return MyService.Instance;
  }
  private constructor() {}
}

Я думал о декораторах, поэтому мой вопрос в том, можем ли мы действительно избавиться от этого Приведенный выше код и использовать декоратор, я попытался несколько неудачных попыток, как:

function service<T>(): T {
  const Instance: T | null = null;
  return !Instance ? new T() : Instance;
}

@service<MyService>()

или

function service(constructor: Function) {
  const Instance: MyService | null = null;
  return !Instance ? new MyService() : Instance;
}
@service

, но они не будут работать. Я не уверен, что декоратор подойдет или нет, здесь может работать другой метод, но у меня нет ни малейшего представления, никаких предложений?

Ответы [ 2 ]

3 голосов
/ 04 мая 2020

возможно, вы можете попробовать следующим образом

singletone.decorator.ts

const serviceList: any[] = [];

export function AsSingletone() {
    return (target: any): void  => {
        if(target.prototype.Instance) {
            return;
        }
        serviceList.push(target);
        Object.defineProperty(target, 'Instance', {
            get: function () {
                if (target.prototype.Instance) {
                    return target.prototype.Instance;
                }
                const instance = new target();
                target.prototype.Instance = instance;
                Object.defineProperty(target, 'Instance',
                  { get: function () { return instance; } }
                );
                return instance;
            }, configurable: true
        });
    };
}

export function registeredServiceList(): any[] {
    return serviceList;
}

service.ts

import { AsSingletone } from "./singletone.decorator";

@AsSingletone()
export class MyService {
  public static readonly Instance: MyService;
}

получить доступ

console.log(MyService.Instance);

set throws исключение

MyService.Instance = (new MyService() as any).Instance as MyService;

пример машинописной игровой площадки

VS Шаблон сниппета кода, начать печатать - singl

"Singletone": {
    "prefix": ["singl"],
    "body": [
        "AsSingletone()\r",
        "export class ${0}Service {\r",
        "\tpublic static readonly Instance: ${0}Service;",
        "}"],
    "description": "Singletone service template"
  }
2 голосов
/ 07 мая 2020

как насчет этого декоратора?

export interface ServiceClassOptions {
    isSingleton: boolean;
}

/* eslint-disable space-before-function-paren */
export function ServiceClass(options: ServiceClassOptions) {
    return (constructor: any) => {
        console.log('from decorator', constructor.prototype.Instance);
        const original = constructor;

        const fun: any = (...args) => {
            if (options.isSingleton) {
                if (constructor.prototype.Instance) {
                    return constructor.prototype.Instance;
                } else {
                    const instance = new constructor(...args);
                    constructor.prototype.Instance = instance;

                    return instance;
                }
            }
            return new constructor(...args);
        };

        fun.prototype = original.prototype;

        return fun;
    };
}

класс декоратора выглядит следующим образом

@ServiceClass({
    isSingleton: true
})
export class AnyService {
}

, затем вызовите new AnyService(); в любое время, когда захотите, и тот же экземпляр вернет, если isSingleton = true

...