Jawaban:
Kelas singleton dalam TypeScript umumnya merupakan anti-pola. Anda bisa menggunakan ruang nama saja.
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()
?
Sejak TS 2.0, kami memiliki kemampuan untuk mendefinisikan pengubah visibilitas pada konstruktor , jadi sekarang kita dapat melakukan lajang dalam TypeScript sama seperti kita terbiasa dari bahasa lain.
Contoh yang diberikan:
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;
Terima kasih @Drenai telah menunjukkan bahwa jika Anda menulis kode menggunakan javascript mentah yang dikompilasi, Anda tidak akan memiliki perlindungan terhadap banyak instantiasi, karena kendala TS menghilang dan konstruktor tidak akan disembunyikan.
Cara terbaik yang saya temukan adalah:
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;
}
}
Inilah cara Anda menggunakannya:
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/
Pendekatan berikut menciptakan kelas Singleton yang dapat digunakan dengan tepat seperti kelas konvensional:
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;
}
Setiap new Singleton()
operasi akan mengembalikan instance yang sama. Namun ini bisa tidak terduga oleh pengguna.
Contoh berikut lebih transparan bagi pengguna tetapi membutuhkan penggunaan yang berbeda:
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;
}
Pemakaian: var obj = Singleton.getInstance();
new Class(...)
sintaksis.
Saya terkejut tidak melihat pola berikut di sini, yang sebenarnya terlihat sangat sederhana.
// shout.ts
class ShoutSingleton {
helloWorld() { return 'hi'; }
}
export let Shout = new ShoutSingleton();
Pemakaian
import { Shout } from './shout';
Shout.helloWorld();
Shout
kelas baru
Anda dapat menggunakan ekspresi kelas untuk ini (pada 1,6 saya percaya).
var x = new (class {
/* ... lots of singleton logic ... */
public someMethod() { ... }
})();
atau dengan nama jika kelas Anda perlu mengakses tipenya secara internal
var x = new (class Singleton {
/* ... lots of singleton logic ... */
public someMethod(): Singleton { ... }
})();
Pilihan lain adalah menggunakan kelas lokal di dalam singleton Anda menggunakan beberapa anggota statis
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();
Tambahkan 6 baris berikut ke kelas mana saja untuk menjadikannya "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
[Sunting]: Gunakan jawaban Alex jika Anda lebih suka mendapatkan instance melalui properti daripada metode.
new MySingleton()
, katakan 5 kali? apakah kode Anda memesan satu instance?
Saya pikir mungkin menggunakan generik menjadi adonan
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;
}
Cara Penggunaan
Langkah 1
class MapManager extends Singleton<MapManager>{
//do something
public init():void{ //do }
}
Langkah 2
MapManager.Instance(MapManager).init();
Anda juga dapat menggunakan fungsi Object.Freeze () . Sederhana dan mudah:
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)
konstruktor? Apakah itu hanya tindakan pencegahan tambahan jika Anda membuat beberapa instance sebelum ekspor?
Saya telah menemukan versi baru dari hal ini bahwa kompiler Typecript benar-benar oke, dan saya pikir lebih baik karena tidak perlu memanggil getInstance()
metode secara terus-menerus.
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;
}
}
Ini memang datang dengan kelemahan yang berbeda. Jika Anda Singleton
memang memiliki properti apa pun, maka kompiler Scripteks akan melempar kecocokan kecuali Anda menginisialisasi mereka dengan nilai. Itu sebabnya saya menyertakan _express
properti dalam kelas contoh saya karena kecuali Anda menginisialisasi dengan nilai, bahkan jika Anda menetapkannya nanti dalam konstruktor, Typcript akan berpikir itu belum didefinisikan. Ini bisa diperbaiki dengan menonaktifkan mode ketat, tapi saya lebih suka tidak jika memungkinkan. Ada juga kelemahan lain dari metode ini yang harus saya tunjukkan, karena konstruktor benar-benar dipanggil, setiap kali ia melakukan instance lain secara teknis dibuat, tetapi tidak dapat diakses. Secara teori, ini bisa menyebabkan kebocoran memori.
Ini mungkin adalah proses terpanjang untuk membuat singleton dalam naskah, tetapi dalam aplikasi yang lebih besar adalah yang telah bekerja lebih baik untuk saya.
Pertama Anda membutuhkan kelas Singleton di, katakanlah, "./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; }
}
}
Sekarang bayangkan Anda membutuhkan Router singleton "./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();
}
Bagus !, sekarang mulai atau impor di mana pun Anda butuhkan:
/// <reference path="./navigation/Router.ts" />
import router = navigation.Router;
router.init();
router.init(); // Throws error!.
Yang menyenangkan tentang melakukan lajang dengan cara ini adalah bahwa Anda masih menggunakan semua keindahan kelas naskah, ini memberi Anda kecerdasan yang bagus, logika tunggal tetap terpisah dan mudah untuk menghapus jika diperlukan.
Solusi saya untuk itu:
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
. Dengan cara ini, jika Anda new
kelas itu, Anda mendapatkan objek yang ada, bukan yang baru.
Dalam naskah, seseorang tidak harus mengikuti new instance()
metodologi Singleton. Kelas statis impor, konstruktor-kurang dapat bekerja sama juga.
Mempertimbangkan:
export class YourSingleton {
public static foo:bar;
public static initialise(_initVars:any):void {
YourSingleton.foo = _initvars.foo;
}
public static doThing():bar {
return YourSingleton.foo
}
}
Anda dapat mengimpor kelas dan merujuk ke YourSingleton.doThing()
kelas lain mana pun. Tapi ingat, karena ini adalah kelas statis, ia tidak memiliki konstruktor jadi saya biasanya menggunakan intialise()
metode yang dipanggil dari kelas yang mengimpor Singleton:
import {YourSingleton} from 'singleton.ts';
YourSingleton.initialise(params);
let _result:bar = YourSingleton.doThing();
Jangan lupa bahwa di kelas statis, setiap metode dan variabel juga harus bersifat statis this
Anda akan menggunakan nama kelas penuh YourSingleton
.
Berikut ini cara lain untuk melakukannya dengan pendekatan javascript yang lebih konvensional menggunakan 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();
Lihat demo
Instance
variabel? Anda hanya dapat menempatkan variabel dan fungsi langsung di bawah App.Counter
.
Pilihan lain adalah menggunakan Simbol dalam modul Anda. Dengan cara ini Anda dapat melindungi kelas Anda, juga jika pengguna akhir API Anda menggunakan 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";
}
}
Pemakaian:
var str:string = Singleton.instance.myFoo();
Jika pengguna menggunakan file kompilasi API js Anda, juga akan mendapatkan kesalahan jika ia mencoba untuk instantiate kelas Anda secara manual:
// PLAIN JAVASCRIPT:
var instance = new Singleton(); //Error the argument singletonToken !== _instance symbol
Ini adalah cara paling sederhana
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();
}
Dengan cara ini kita dapat menerapkan antarmuka, tidak seperti jawaban yang diterima Ryan Cavanaugh.
Setelah menjelajahi utas ini dan bermain-main dengan semua opsi di atas - Saya memilih Singleton yang dapat dibuat dengan konstruktor yang tepat:
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 */ }
}
Ini akan membutuhkan pengaturan awal (dalam main.ts
, atau index.ts
), yang dapat dengan mudah diimplementasikan oleh
new Singleton(/* PARAMS */)
Lalu, di mana pun dalam kode Anda, panggil saja Singleton.instnace
; dalam hal ini, untuk work
diselesaikan, saya akan meneleponSingleton.instance.work()