Réponses:
Les classes Singleton dans TypeScript sont généralement un anti-pattern. Vous pouvez simplement utiliser des espaces de noms à la place.
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
export default new Singleton()
?
Depuis TS 2.0, nous avons la possibilité de définir des modificateurs de visibilité sur les constructeurs , donc maintenant nous pouvons faire des singletons dans TypeScript comme nous en avons l'habitude dans d'autres langages.
Exemple donné:
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;
Merci @Drenai d'avoir souligné que si vous écrivez du code en utilisant le javascript compilé brut, vous n'aurez pas de protection contre l'instanciation multiple, car les contraintes de TS disparaissent et le constructeur ne sera pas masqué.
Le meilleur moyen que j'ai trouvé est:
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;
}
}
Voici comment vous l'utilisez:
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/
L'approche suivante crée une classe Singleton qui peut être utilisée exactement comme une classe conventionnelle:
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;
}
Chaque new Singleton()
opération renverra la même instance. Cela peut cependant être inattendu par l'utilisateur.
L'exemple suivant est plus transparent pour l'utilisateur mais nécessite une utilisation différente:
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();
new Class(...)
syntaxe.
Je suis surpris de ne pas voir le modèle suivant ici, qui semble en fait très simple.
// shout.ts
class ShoutSingleton {
helloWorld() { return 'hi'; }
}
export let Shout = new ShoutSingleton();
Usage
import { Shout } from './shout';
Shout.helloWorld();
Shout
Vous pouvez utiliser des expressions de classe pour cela (à partir de 1.6 je crois).
var x = new (class {
/* ... lots of singleton logic ... */
public someMethod() { ... }
})();
ou avec le nom si votre classe a besoin d'accéder à son type en interne
var x = new (class Singleton {
/* ... lots of singleton logic ... */
public someMethod(): Singleton { ... }
})();
Une autre option consiste à utiliser une classe locale à l'intérieur de votre singleton en utilisant des membres statiques
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();
Ajoutez les 6 lignes suivantes à n'importe quelle classe pour en faire un "Singleton".
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
[Edit]: Utilisez Alex answer si vous préférez obtenir l'instance via une propriété plutôt qu'une méthode.
new MySingleton()
, disons 5 fois? votre code réserve-t-il une seule instance?
je pense que peut-être utiliser des génériques
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;
}
comment utiliser
étape 1
class MapManager extends Singleton<MapManager>{
//do something
public init():void{ //do }
}
étape 2
MapManager.Instance(MapManager).init();
Vous pouvez également utiliser la fonction Object.Freeze () . C'est simple et facile:
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;
if (!this.instance)
dans le constructeur? Est-ce juste une précaution supplémentaire au cas où vous auriez créé plusieurs instances avant l'exportation?
J'ai trouvé une nouvelle version de ceci que le compilateur Typescript est tout à fait d'accord, et je pense qu'elle est meilleure car elle ne nécessite pas d'appeler une getInstance()
méthode en permanence.
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;
}
}
Cela présente un inconvénient différent. Si vous Singleton
avez des propriétés, le compilateur Typescript lèvera un ajustement à moins que vous ne les initialisiez avec une valeur. C'est pourquoi j'ai inclus une _express
propriété dans mon exemple de classe car à moins que vous ne l'initialisiez avec une valeur, même si vous l'assignez plus tard dans le constructeur, Typescript pensera qu'elle n'a pas été définie. Cela pourrait être résolu en désactivant le mode strict, mais je préfère ne pas le faire si possible. Il y a aussi un autre inconvénient à cette méthode que je dois souligner, car le constructeur est en fait appelé, chaque fois qu'il le fait, une autre instance est techniquement créée, mais pas accessible. Cela pourrait, en théorie, provoquer des fuites de mémoire.
C'est probablement le processus le plus long pour créer un singleton en tapuscrit, mais dans les applications plus importantes, c'est celui qui a le mieux fonctionné pour moi.
Vous avez d'abord besoin d'une classe Singleton dans, disons, "./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; }
}
}
Imaginez maintenant que vous ayez besoin d'un singleton de routeur "./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();
}
Nice !, maintenant initialisez ou importez où vous en avez besoin:
/// <reference path="./navigation/Router.ts" />
import router = navigation.Router;
router.init();
router.init(); // Throws error!.
L'avantage de faire des singletons de cette façon est que vous utilisez toujours toute la beauté des classes dactylographiées, cela vous donne une belle intelligence, la logique des singleton reste en quelque sorte séparée et elle est facile à supprimer si nécessaire.
Ma solution pour cela:
export default class Modal {
private static _instance : Modal = new Modal();
constructor () {
if (Modal._instance)
throw new Error("Use Modal.instance");
Modal._instance = this;
}
static get instance () {
return Modal._instance;
}
}
return Modal._instance
. De cette façon, si vous new
utilisez cette classe, vous obtenez l'objet existant, pas un nouveau.
Dans Typescript, il n'est pas nécessaire de suivre les new instance()
méthodologie Singleton. Une classe statique importée sans constructeur peut également fonctionner.
Considérer:
export class YourSingleton {
public static foo:bar;
public static initialise(_initVars:any):void {
YourSingleton.foo = _initvars.foo;
}
public static doThing():bar {
return YourSingleton.foo
}
}
Vous pouvez importer la classe et y faire référence YourSingleton.doThing()
dans n'importe quelle autre classe. Mais rappelez-vous, comme il s'agit d'une classe statique, elle n'a pas de constructeur, donc j'utilise généralement une intialise()
méthode appelée à partir d'une classe qui importe le Singleton:
import {YourSingleton} from 'singleton.ts';
YourSingleton.initialise(params);
let _result:bar = YourSingleton.doThing();
N'oubliez pas que dans une classe statique, chaque méthode et variable doit également être statique, donc au lieu d' this
utiliser le nom complet de la classe YourSingleton
.
Voici encore une autre façon de le faire avec une approche javascript plus conventionnelle utilisant un IFFE :
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();
Voir une démo
Instance
variable? Vous pouvez simplement mettre la variable et les fonctions directement sous App.Counter
.
Une autre option consiste à utiliser des symboles dans votre module. De cette façon, vous pouvez protéger votre classe, même si l'utilisateur final de votre API utilise du Javascript normal:
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";
}
}
Usage:
var str:string = Singleton.instance.myFoo();
Si l'utilisateur utilise votre fichier API js compilé, il obtiendra également une erreur s'il tente d'instancier manuellement votre classe:
// PLAIN JAVASCRIPT:
var instance = new Singleton(); //Error the argument singletonToken !== _instance symbol
C'est le moyen le plus simple
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;
}
}
namespace MySingleton {
interface IMySingleton {
doSomething(): void;
}
class MySingleton implements IMySingleton {
private usePrivate() { }
doSomething() {
this.usePrivate();
}
}
export var Instance: IMySingleton = new MySingleton();
}
De cette façon, nous pouvons appliquer une interface, contrairement à la réponse acceptée de Ryan Cavanaugh.
Après avoir parcouru ce fil et joué avec toutes les options ci-dessus, je me suis installé avec un Singleton qui peut être créé avec les constructeurs appropriés:
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 */ }
}
Cela nécessiterait une configuration initiale (dans main.ts
ou index.ts
), qui peut facilement être implémentée par
new Singleton(/* PARAMS */)
Ensuite, n'importe où dans votre code, appelez simplement Singleton.instnace
; dans ce cas, pour work
faire, j'appelleraisSingleton.instance.work()