Yanıtlar:
TypeScript'teki Singleton sınıfları genellikle bir anti-modeldir. Bunun yerine ad alanlarını kullanabilirsiniz .
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()?
TS 2.0'dan beri , yapıcılarda görünürlük değiştiricileri tanımlama yeteneğine sahibiz , bu yüzden artık diğer dillerde alıştığımız gibi TypeScript'te tekli tonlar yapabiliriz.
Verilen örnek:
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, eğer derlenmiş ham javascript kullanarak kod yazarsanız, TS kısıtlamaları ortadan kalktığından ve kurucu gizlenmeyeceğinden, çoklu somutlaştırmaya karşı korumanız olmayacağını belirttiğiniz için teşekkür ederiz.
Bulduğum en iyi yol:
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;
}
}
İşte onu nasıl kullanıyorsun:
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/
Aşağıdaki yaklaşım, geleneksel bir sınıf gibi tam olarak kullanılabilen bir Singleton sınıfı oluşturur:
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;
}
Her new Singleton()işlem aynı örneği döndürecektir. Ancak bu, kullanıcı tarafından beklenmedik olabilir.
Aşağıdaki örnek, kullanıcı için daha şeffaftır ancak farklı bir kullanım gerektirir:
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;
}
Kullanımı: var obj = Singleton.getInstance();
new Class(...)sözdizimi uygulamalıdır .
Aslında çok basit görünen aşağıdaki kalıbı burada görmemek beni şaşırttı.
// shout.ts
class ShoutSingleton {
helloWorld() { return 'hi'; }
}
export let Shout = new ShoutSingleton();
kullanım
import { Shout } from './shout';
Shout.helloWorld();
Shoutsınıf oluşturmaktan
Bunun için sınıf ifadelerini kullanabilirsiniz (inanıyorum 1.6'dan itibaren).
var x = new (class {
/* ... lots of singleton logic ... */
public someMethod() { ... }
})();
veya sınıfınızın türüne dahili olarak erişmesi gerekiyorsa adı ile
var x = new (class Singleton {
/* ... lots of singleton logic ... */
public someMethod(): Singleton { ... }
})();
Başka bir seçenek, bazı statik üyeler kullanarak tekliğinizin içinde yerel bir sınıf kullanmaktır.
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();
Aşağıdaki 6 satırı herhangi bir sınıfa "Singleton" yapmak için ekleyin.
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
[Düzenle]: Örneği bir yöntem yerine bir özellik aracılığıyla almayı tercih ediyorsanız, Alex yanıtını kullanın.
new MySingleton()5 kere yaparsam ne olur ? kodunuz tek bir örnek mi ayırıyor?
Sanırım jenerikler daha meyilli olabilir
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;
}
nasıl kullanılır
Aşama 1
class MapManager extends Singleton<MapManager>{
//do something
public init():void{ //do }
}
Adım 2
MapManager.Instance(MapManager).init();
Ayrıca Object.Freeze () işlevini de kullanabilirsiniz . Basit ve kolaydır:
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)? Bu, dışa aktarmadan önce birden çok örnek oluşturduğunuzda alacağınız ekstra bir önlem mi?
Bunun, Typescript derleyicisinin tamamen uygun olduğu yeni bir versiyonunu buldum ve bence daha iyi çünkü getInstance()sürekli bir metodu çağırmayı gerektirmiyor .
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;
}
}
Bunun farklı bir dezavantajı var. Sizin Singletonherhangi bir özelliğiniz varsa, Typescript derleyicisi siz onları bir değerle başlatmadığınız sürece bir uyum oluşturacaktır. Bu yüzden _expressörnek sınıfıma bir özellik ekledim çünkü onu bir değerle başlatmadığınız sürece, daha sonra yapıcıda atasanız bile, Typescript tanımlanmadığını düşünecek. Bu, katı modu devre dışı bırakarak düzeltilebilir, ancak mümkünse yapmamayı tercih ederim. Bu yöntemin başka bir dezavantajı daha var, çünkü kurucu aslında çağrılıyor, her seferinde başka bir örnek teknik olarak yaratılıyor, ancak erişilebilir değil. Bu teorik olarak bellek sızıntılarına neden olabilir.
Bu muhtemelen daktiloda bir singleton yapmak için en uzun süreçtir, ancak daha büyük uygulamalarda benim için daha iyi sonuç veren süreçtir.
Önce "./utils/Singleton.ts" diyelim ki içinde bir Singleton sınıfına ihtiyacınız var :
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; }
}
}
Şimdi tek bir Yönlendirici "./navigation/Router.ts" dosyasına ihtiyacınız olduğunu hayal edin :
/// <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();
}
Güzel !, şimdi ihtiyacınız olan her yerde başlatın veya içe aktarın:
/// <reference path="./navigation/Router.ts" />
import router = navigation.Router;
router.init();
router.init(); // Throws error!.
Tekilleri bu şekilde yapmanın güzel yanı, hala daktilo sınıflarının tüm güzelliğini kullanıyor olmanızdır, size güzel bir zeka verir, tekli mantık bir şekilde ayrı kalır ve gerekirse çıkarması kolaydır.
Benim çözümüm:
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. Bu şekilde, eğer newo sınıfsanız, yeni bir nesneyi değil, mevcut nesneyi alırsınız.
Typescript'te mutlaka new instance()Singleton metodolojisini takip etmek zorunda değilsiniz . İçe aktarılan, yapıcısız statik bir sınıf da eşit şekilde çalışabilir.
Düşünmek:
export class YourSingleton {
public static foo:bar;
public static initialise(_initVars:any):void {
YourSingleton.foo = _initvars.foo;
}
public static doThing():bar {
return YourSingleton.foo
}
}
Sınıfı içe aktarabilir ve YourSingleton.doThing()başka herhangi bir sınıfta başvurabilirsiniz . Ancak unutmayın, bu statik bir sınıf olduğundan, yapıcısı yoktur, bu nedenle genellikle intialise()Singleton'u içe aktaran bir sınıftan çağrılan bir yöntem kullanırım :
import {YourSingleton} from 'singleton.ts';
YourSingleton.initialise(params);
let _result:bar = YourSingleton.doThing();
Statik bir sınıfta, her yöntemin ve değişkenin de statik olması gerektiğini unutmayın, böylece thistam sınıf adını kullanmak yerine sizin yerinize YourSingleton.
Bunu bir IFFE kullanarak daha geleneksel bir javascript yaklaşımıyla yapmanın bir başka yolu daha :
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();
Bir demo izleyin
InstanceDeğişkeni eklemenin nedeni nedir ? Sadece değişkeni ve fonksiyonları doğrudan altına koyun App.Counter.
Diğer bir seçenek de modülünüzde Semboller kullanmaktır. Bu şekilde, API'nizin son kullanıcısı normal Javascript kullanıyorsa da sınıfınızı koruyabilirsiniz:
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";
}
}
Kullanımı:
var str:string = Singleton.instance.myFoo();
Kullanıcı derlenmiş API js dosyanızı kullanıyorsa, sınıfınızı manuel olarak başlatmaya çalıştığında da bir hata alır:
// PLAIN JAVASCRIPT:
var instance = new Singleton(); //Error the argument singletonToken !== _instance symbol
Bu en basit yol
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();
}
Bu şekilde Ryan Cavanaugh'un kabul ettiği cevabın aksine bir arayüz uygulayabiliriz.
Bu konuyu temizledikten ve yukarıdaki tüm seçeneklerle oynadıktan sonra - uygun kurucularla oluşturulabilen bir Singleton ile anlaştım:
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 */ }
}
Bir başlangıç kurulumu (içinde main.ts, veya index.ts) gerektirir ve bu, tarafından kolayca uygulanabilir.
new Singleton(/* PARAMS */)
Ardından, kodunuzun herhangi bir yerinde arayın Singleton.instnace; bu durumda, bitirmek için workarardımSingleton.instance.work()