问题
I am building a project using VueJS with Typescript. I feel comfortable using services instead of any state management library like Vuex. But when writing services I have to always copy paste some code in each service class, to make it singelton as:
class MyService {
private static Instance: MyService;
public static getInstance() {
if (!MyService.Instance) {
MyService.Instance = new MyService();
}
return MyService.Instance;
}
private constructor() {}
}
I was thinking about the decorators, so my question is can we really get rid of this above code and use decorator, I tried some failed attempts as:
function service<T>(): T {
const Instance: T | null = null;
return !Instance ? new T() : Instance;
}
@service<MyService>()
or
function service(constructor: Function) {
const Instance: MyService | null = null;
return !Instance ? new MyService() : Instance;
}
@service
but these are not going to work. I am not sure that decorator will do the trick or not, other method may work here, but I don't have any idea, any suggestions?
回答1:
maybe you can try as follow
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;
}
get access
console.log(MyService.Instance);
set throws exception
MyService.Instance = (new MyService() as any).Instance as MyService;
typescript playground example
VS Code snippet template, start typing - singl
"Singletone": {
"prefix": ["singl"],
"body": [
"AsSingletone()\r",
"export class ${0}Service {\r",
"\tpublic static readonly Instance: ${0}Service;",
"}"],
"description": "Singletone service template"
}
回答2:
what about this decorator?
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;
};
}
decorator class as follows
@ServiceClass({
isSingleton: true
})
export class AnyService {
}
then call new AnyService();
anytime you want will return same instance if isSingleton = true
来源:https://stackoverflow.com/questions/61582349/make-decorator-for-singelton-class-typescript