93 votes

ES6 Singleton vs Instanciation unique d'une classe

Je vois des modèles qui utilisent un modèle de singleton avec des classes ES6 et je me demande pourquoi je les utiliserais plutôt que de simplement instancier la classe au bas du fichier et d'exporter l'instance. Y a-t-il une sorte d'inconvénient à faire cela ? Par exemple :

Instance d'exportation ES6 :

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;

Utilisation :

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

Quelle est la différence entre l'utilisation du modèle Singleton suivant ? Y a-t-il des raisons d'utiliser l'un plutôt que l'autre ? Je suis en fait plus curieux de savoir si le premier exemple que j'ai donné peut présenter des problèmes dont je ne suis pas conscient.

Modèle Singleton :

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;

Utilisation :

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

let api = new API()

67voto

Bergi Points 104242

Je ne recommanderais ni l'un ni l'autre. C'est totalement surcompliqué. Si vous n'avez besoin que d'un seul objet, n'utilisez pas le class syntaxe ! Allez-y pour

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'

ou encore plus simple

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'

3 votes

Voici la manière correcte et idiomatique de faire cela en js

26 votes

Notez que javascript a toujours eu des singletons intégrés au langage. Nous ne les appelons simplement pas singletons, nous les appelons object literals. Ainsi, chaque fois que vous avez besoin d'une seule instance d'un objet, les programmeurs js créent automatiquement un objet littéral. En js, beaucoup de choses qui sont des "design patterns" dans d'autres langages sont des syntaxes intégrées.

0 votes

Je dirais que ce n'est pas approprié si vous voulez injecter comme dépendances le fetch, ainsi il est plus facile à tester.

47voto

Zlatko Points 2723

La différence réside dans le fait que vous voulez tester des choses.

Disons que vous avez api.spec.js fichier de test. Et que votre API a une dépendance, comme ces constantes.

Spécifiquement, le constructeur dans vos deux versions prend un paramètre, votre Constants l'importation.

Ainsi, votre constructeur ressemble à ceci :

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.
    });
});

Maintenant, avec l'instance d'exportation, il n'y a pas de mocking.

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

Avec un objet instancié exporté, vous ne pouvez pas (facilement et sainement) changer son comportement.

6 votes

Les développeurs Javascript ont tendance à coder en dur toutes leurs dépendances via l'importation pour une raison quelconque. Je suis d'accord sur le fait que c'est une meilleure pratique de passer les dépendances via le constructeur pour que ce soit a) testable, b) réutilisable.

2 votes

J'ai récompensé cette réponse car elle répond effectivement à ma question initiale. Merci.

0 votes

"// erm... now what ?" pourquoi pas ? API.url = MockConstants.API_URL ; L'objet a toutes ces propriétés/méthodes d'instance, quelle que soit la classe qui y avait accès avec "this" . Mais bien sûr, la mutation donnerait lieu à d'autres problèmes dans les tests unitaires.

11voto

Sumer Points 598

Ces deux méthodes sont différentes. Exportation d'une classe comme ci-dessous

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

puis l'importer comme ci-dessous dans plusieurs fichiers indiquerait la même instance et une façon de créer le modèle Singleton.

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

Alors que l'autre façon d'exporter la classe directement n'est pas singleton car dans le fichier où nous importons, nous devons créer une nouvelle classe et cela créera une instance séparée pour chaque nouvelle classe. Exportation de la classe uniquement :

export default API;

Importation de la classe et mise à jour

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

2voto

Peter Points 31

Une autre raison d'utiliser le modèle Singleton est que dans certains cadres (comme le Polymer 1.0 ), vous ne pouvez pas utiliser export la syntaxe.
C'est pourquoi la deuxième option (modèle Singleton) est plus utile, pour moi.

J'espère que cela vous aidera.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X