【问题标题】:Make decorator for singelton class -- typescript为单例类做装饰——打字稿
【发布时间】:2020-05-07 05:34:22
【问题描述】:

我正在使用带有 Typescript 的 VueJS 构建一个项目。我觉得使用服务而不是像 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

但这些都行不通。我不确定装饰器是否会成功,其他方法可能在这里有效,但我不知道,有什么建议吗?

【问题讨论】:

  • 我没有答案,但我喜欢这个问题!

标签: typescript decorator


【解决方案1】:

也许你可以尝试如下

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 抛出异常

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

typescript playground example

VS Code sn -p 模板,开始输入-singl

"Singletone": {
    "prefix": ["singl"],
    "body": [
        "AsSingletone()\r",
        "export class ${0}Service {\r",
        "\tpublic static readonly Instance: ${0}Service;",
        "}"],
    "description": "Singletone service template"
  }

【讨论】:

  • registeredServiceList 如果您需要在示例中获取所有单例进行调试
  • @user12061795 defineProperty 通常优化并以相同的速度工作,但没有 if() 的实现工作得更快。我通过一些测试添加到操场的链接
  • @user12061795 你想在什么时候初始化它?
  • @user12061795 我更新了装饰器,它将在第一次调用 MyService.Instance 时初始化
  • @user12061795 是的,首先调用 MyService.Instance 将初始化属性
【解决方案2】:

这个装饰器怎么样?

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 将返回相同的实例

【讨论】:

    猜你喜欢
    • 2018-02-12
    • 2020-05-31
    • 2016-12-05
    • 2020-01-31
    • 2017-04-02
    • 2019-03-25
    • 2020-01-02
    • 2018-06-21
    • 2019-07-29
    相关资源
    最近更新 更多