ES6 Singleton im Vergleich zum einmaligen Instanziieren einer Klasse

Lesezeit: 8 Minuten

Ich sehe Muster, die ein Singleton-Muster mit ES6-Klassen verwenden, und ich frage mich, warum ich sie verwenden würde, anstatt nur die Klasse am Ende der Datei zu instanziieren und die Instanz zu exportieren. Gibt es irgendeine Art von negativem Nachteil, dies zu tun? Zum Beispiel:

ES6-Exportinstanz:

import Constants from '../constants';

class _API {
  constructor() {
    this.url = Constants.API_URL;
  }

  getCities() {
    return fetch(this.url, { method: 'get' })
      .then(response => response.json());
  }
}

const API = new _API();
export default API;

Verwendungszweck:

import API from './services/api-service'

Was ist der Unterschied zur Verwendung des folgenden Singleton-Musters? Gibt es Gründe, das eine vom anderen zu verwenden? Ich bin eigentlich eher neugierig zu wissen, ob das erste Beispiel, das ich gegeben habe, Probleme haben kann, die mir nicht bewusst sind.

Singleton-Muster:

import Constants from '../constants';

let instance = null;

class API {
  constructor() {

    if(!instance){
      instance = this;
    }

    this.url = Constants.API_URL;

    return instance;
  }

  getCities() {
    return fetch(this.url, { method: 'get' })
      .then(response => response.json());
  }
}

export default API;

Verwendungszweck:

import API from './services/api-service';

let api = new API()

Ich würde beides nicht empfehlen. Das ist total zu kompliziert. Wenn Sie nur ein Objekt benötigen, verwenden Sie die nicht class Syntax! Gehen Sie einfach hin

import Constants from '../constants';

export default {
  url: Constants.API_URL,
  getCities() {
    return fetch(this.url, { method: 'get' }).then(response => response.json());
  }
};

import API from './services/api-service'

oder noch einfacher

import Constants from '../constants';

export const url = Constants.API_URL;
export function getCities() {
  return fetch(url, { method: 'get' }).then(response => response.json());
}

import * as API from './services/api-service'

  • Dies ist der richtige, idiomatische Weg, dies in js zu tun

    – Schlafmann

    21. Januar 2018 um 15:59 Uhr

  • Beachten Sie, dass Javascript immer Singletons in die Sprache integriert hat. Wir nennen sie einfach nicht Singletons, wir nennen sie Objektliterale. Wann immer Sie also eine einzelne Instanz eines Objekts benötigen, erstellen js-Programmierer automatisch ein Objektliteral. In js sind viele Dinge, die in anderen Sprachen “Entwurfsmuster” sind, integrierte Syntax.

    – Schlafmann

    21. Januar 2018 um 16:02 Uhr

  • Ich würde sagen, dass dies nicht angemessen ist, wenn Sie den Abruf als Abhängigkeiten einfügen möchten, damit es einfacher zu testen ist.

    – Cesar Alberca

    18. Dezember 2018 um 19:43 Uhr

  • @CésarAlberca OP hat keine Abhängigkeitsinjektion verwendet, also habe ich das nicht berücksichtigt. Und trotzdem bräuchten Sie kein class Dafür sollte ein Modulimport oder eine Factory-Funktion ausreichen fetch verspottbar.

    – Bergi

    18. Dezember 2018 um 19:56 Uhr

  • @codewise Der Link in der Antwort erklärt warum class Syntax sollte für Singleton-Objekte vermieden werden.

    – Bergi

    3. April 2019 um 10:39 Uhr

Benutzer-Avatar
Zlatko

Der Unterschied ist, wenn Sie Dinge testen möchten.

Sagen Sie, Sie haben api.spec.js Testdatei. Und dass Ihr API-Ding eine Abhängigkeit hat, wie diese Konstanten.

Insbesondere nimmt der Konstruktor in beiden Versionen einen Parameter, your Constants importieren.

Ihr Konstruktor sieht also so aus:

class API {
    constructor(constants) {
      this.API_URL = constants.API_URL;
    }
    ...
}



// single-instance method first
import API from './api';
describe('Single Instance', () => {
    it('should take Constants as parameter', () => {
        const mockConstants = {
            API_URL: "fake_url"
        }
        const api = new API(mockConstants); // all good, you provided mock here.
    });
});

Jetzt, mit dem Exportieren von Instanzen, gibt es kein Spott mehr.

import API from './api';
describe('Singleton', () => {
    it('should let us mock the constants somehow', () => {
        const mockConstants = {
            API_URL: "fake_url"
        }
        // erm... now what?
    });
});

Wenn das instanziierte Objekt exportiert wurde, können Sie sein Verhalten nicht (einfach und vernünftig) ändern.

  • Javascript-Entwickler neigen dazu, alle ihre Abhängigkeiten aus irgendeinem Grund per Import fest zu codieren. Ich stimme zu, dass es besser ist, Abhängigkeiten über den Konstruktor zu übergeben, damit er a) testbar und b) wiederverwendbar ist.

    – Josh Stuart

    27. April 2018 um 5:04 Uhr

  • Ich habe diese Antwort vergeben, weil sie meine ursprüngliche Frage tatsächlich beantwortet. Vielen Dank.

    – Aaron

    18. Juni 2018 um 16:19 Uhr

  • “// ähm… was jetzt?” warum nicht? API.url = MockConstants.API_URL; Das Objekt hat alle diese Instanzeigenschaften/Methoden, egal welche Klasse mit “this” Zugriff hatte. Aber natürlich würde eine Mutation zu anderen Problemen in Unit-Tests führen

    – Shishir Arora

    6. April 2021 um 21:39 Uhr


  • @ShishirArora das Problem ist damit. Sie haben einen Test, wo Sie das behaupten API.url === 'example.com'. Alles gut. Dann fügt das jemand ein API.url === 'something else' vor Ihrem Test – Sie ändern die API Objekt für die gesamte Testsuite, nicht nur für eine einzelne Testinstanz. Jetzt haben Sie andere Tests gebrochen – obwohl Sie (möglicherweise) den Code selbst nicht gebrochen haben.

    – Zlatko

    8. April 2021 um 9:52 Uhr

  • Das sollte in allen Testanzügen Teil des Teardown-Betriebs sein. Sie sollten alle Nebeneffekte eines Tests beseitigen, bevor Sie einen neuen starten. Die Parallelisierung von Tests wäre jedoch ein Problem.

    – Shishir Arora

    16. April 2021 um 15:21 Uhr

Benutzer-Avatar
Sumer

Beides sind unterschiedliche Wege. Exportieren einer Klasse wie unten

const APIobj = new _API();
export default APIobj;   //shortcut=> export new _API()

und dann würde das Importieren wie unten in mehrere Dateien auf dieselbe Instanz und eine Möglichkeit zum Erstellen eines Singleton-Musters verweisen.

import APIobj from './services/api-service'

Während die andere Möglichkeit, die Klasse direkt zu exportieren, nicht Singleton ist, wie in der Datei, in die wir importieren, müssen wir die Klasse neu erstellen, und dies erstellt eine separate Instanz für jede neu erstellte Klasse.

export default API;

Klasse importieren und neu erstellen

import API from './services/api-service';
let api = new API()

Benutzer-Avatar
Peter

Ein weiterer Grund für die Verwendung von Singleton Pattern liegt in einigen Frameworks (wie z Polymer 1.0) können Sie nicht verwenden export Syntax.
Deshalb ist die zweite Option (Singleton-Muster) für mich nützlicher.

Ich hoffe es hilft.

Benutzer-Avatar
khalid

Vielleicht bin ich zu spät, weil diese Frage 2018 geschrieben wurde, aber sie erscheint immer noch oben auf der Ergebnisseite, wenn nach js singleton class gesucht wird, und ich denke, dass sie immer noch nicht die richtige Antwort hat, auch wenn die anderen Möglichkeiten funktionieren. aber erstelle keine Klasseninstanz. Und so erstelle ich eine JS-Singleton-Klasse:

class TestClass {
    static getInstance(dev = true) {
        if (!TestClass.instance) {
            console.log('Creating new instance');
            Object.defineProperty(TestClass, 'instance', {
                value: new TestClass(dev),
                writable : false,
                enumerable : true,
                configurable : false
            });
        } else {
            console.log('Instance already exist');
        }
        return TestClass.instance;
    }
    random;
    constructor() {
        this.random = Math.floor(Math.random() * 99999);
    }
}
const instance1 = TestClass.getInstance();
console.log(`The value of random var of instance1 is: ${instance1.random}`);
const instance2 = TestClass.getInstance();
console.log(`The value of random var of instance2 is: ${instance2.random}`);

Und dies ist das Ergebnis der Ausführung dieses Codes.

Creating new instance
The value of random var of instance1 is: 14929
Instance already exist
The value of random var of instance2 is: 14929

Hoffe das kann jemandem helfen

  • Das Singleton-Muster im OP ist besser als dieses. Ihr Code hat das Problem, dass a) der Konstruktor noch öffentlich ist und von jedem aufgerufen werden kann b) .instance ist öffentlich und kann von jedem geändert werden (einschließlich Zurücksetzen auf null) c) getInstance(false) kann manchmal eine dev-Instanz und manchmal eine prod-Instanz zurückgeben, völlig unabhängig vom übergebenen Argument. Dies ist ein erstklassiges pädagogisches Beispiel dafür, warum man Singletons überhaupt nicht verwenden sollte.

    – Bergi

    31. März um 16:09 Uhr

  • Ja, es ist ein guter Standpunkt, aber Sie können dieses Problem einfach mit definePropriety lösen und es (statische Instanz-Variable) unveränderlich machen, dies ist ein Beispiel: // statische Instanz; //kommentiertes statisches getInstance(dev = true) { if (!TestClass.instance) { Object.defineProperty(TestClass, ‘instance’, { value: new TestClass(dev), writable : false, enumerable : true, configurable : false } ); Danach können Sie die Instanz nicht mehr ändern. Ich werde den Code aktualisieren.

    – khalid

    4. April um 18:52 Uhr


  • aber ich habe nicht verstanden, was du mit “c) getInstance (false) meinst, kann manchmal eine dev-Instanz und manchmal eine prod-Instanz zurückgeben”

    – khalid

    4. April um 18:56 Uhr

  • Das meine ich beim Anrufen getInstance(false) man würde erwarten, dass dies immer eine prod-Instanz zurückgibt, oder? Aber das stimmt nicht, es könnte auch eine Dev-Instanz zurückgeben, abhängig vom Rest des Codes – gruselig Aktion auf Distanz. Ein Singleton-Getter sollte niemals Parameter haben.

    – Bergi

    4. April um 19:01 Uhr


  • Sie haben Recht, Singleton mit Param ist kein Singleton, aber ich habe es so gemacht, um zu zeigen, dass sich der Objektparameter selbst beim Versuch, eine neue Instanz zu erstellen, nicht geändert hat, aber ich werde den Code aktualisieren.

    – khalid

    4. April um 19:34 Uhr

  • Das Singleton-Muster im OP ist besser als dieses. Ihr Code hat das Problem, dass a) der Konstruktor noch öffentlich ist und von jedem aufgerufen werden kann b) .instance ist öffentlich und kann von jedem geändert werden (einschließlich Zurücksetzen auf null) c) getInstance(false) kann manchmal eine dev-Instanz und manchmal eine prod-Instanz zurückgeben, völlig unabhängig vom übergebenen Argument. Dies ist ein erstklassiges pädagogisches Beispiel dafür, warum man Singletons überhaupt nicht verwenden sollte.

    – Bergi

    31. März um 16:09 Uhr

  • Ja, es ist ein guter Standpunkt, aber Sie können dieses Problem einfach mit definePropriety lösen und es (statische Instanz-Variable) unveränderlich machen, dies ist ein Beispiel: // statische Instanz; //kommentiertes statisches getInstance(dev = true) { if (!TestClass.instance) { Object.defineProperty(TestClass, ‘instance’, { value: new TestClass(dev), writable : false, enumerable : true, configurable : false } ); Danach können Sie die Instanz nicht mehr ändern. Ich werde den Code aktualisieren.

    – khalid

    4. April um 18:52 Uhr


  • aber ich habe nicht verstanden, was du mit “c) getInstance (false) meinst, kann manchmal eine dev-Instanz und manchmal eine prod-Instanz zurückgeben”

    – khalid

    4. April um 18:56 Uhr

  • Das meine ich beim Anrufen getInstance(false) man würde erwarten, dass dies immer eine prod-Instanz zurückgibt, oder? Aber das stimmt nicht, es könnte auch eine Dev-Instanz zurückgeben, abhängig vom Rest des Codes – gruselig Aktion auf Distanz. Ein Singleton-Getter sollte niemals Parameter haben.

    – Bergi

    4. April um 19:01 Uhr


  • Sie haben Recht, Singleton mit Param ist kein Singleton, aber ich habe es so gemacht, um zu zeigen, dass sich der Objektparameter selbst beim Versuch, eine neue Instanz zu erstellen, nicht geändert hat, aber ich werde den Code aktualisieren.

    – khalid

    4. April um 19:34 Uhr

1180470cookie-checkES6 Singleton im Vergleich zum einmaligen Instanziieren einer Klasse

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy