35 votes

Injection de dépendances avec RequireJS

Jusqu'à quel point puis-je étendre RequireJS pour fournir une injection de dépendance pour mon application ? Par exemple, disons que j'ai un modèle que je veux être un singleton. Pas un singleton de type getInstance()-auto renforcé, mais un singleton renforcé par le contexte (une instance par "contexte"). J'aimerais faire quelque chose comme...

require(['mymodel'], function(mymodel) {
   ...
}

Et que mymodel soit une instance de la classe MyModel. Si je devais faire cela dans plusieurs modules, je voudrais que mymodel soit la même instance partagée.

J'ai réussi à le faire fonctionner en créant le module mymodel de la manière suivante :

define(function() {
    var MyModel = function() {
        this.value = 10;
    }
    return new MyModel();
});

Est-ce que ce type d'utilisation est attendu et commun ou est-ce que j'abuse de RequireJS ? Y a-t-il une façon plus appropriée d'effectuer une injection de dépendance avec RequireJS ? Merci pour votre aide. J'essaie toujours de comprendre.

60voto

Domenic Points 40761

Ce n'est pas vraiment une injection de dépendance, mais plutôt une localisation de service : vos autres modules demandent une "classe" par une chaîne de caractères "clé", et obtiennent en retour une instance de celle-ci que le "localisateur de service" (dans ce cas RequireJS) a été câblé pour leur fournir.

L'injection de dépendances consisterait à renvoyer le MyModel constructeur, c'est-à-dire return MyModel puis dans un centre composition Racine en injectant une instance de MyModel dans d'autres instances. J'ai mis en place un exemple de ce fonctionnement ici : https://gist.github.com/1274607 (également cité ci-dessous)

De cette façon, la racine de composition détermine si elle doit distribuer une seule instance de MyModel (i.e. le rendre singleton scoped) ou de nouveaux pour chaque classe qui le requiert (instance scoped), ou quelque chose entre les deux. Cette logique n'appartient ni à la définition de MyModel, ni aux classes qui en demandent une instance.

(Note annexe : bien que je ne l'ai pas utilisé, wire.js est un conteneur d'injection de dépendances à part entière pour JavaScript qui a l'air plutôt cool).


Vous n'abusez pas nécessairement de RequireJS en l'utilisant comme vous le faites, bien que ce que vous faites semble un peu détourné, c'est-à-dire déclarer une classe et retourner une nouvelle instance de celle-ci. Pourquoi ne pas simplement faire ce qui suit ?

define(function () {
    var value = 10;

    return {
        doStuff: function () {
            alert(value);
        }
    };
});

L'analogie qui vous échappe peut-être est que les modules sont équivalents à des "espaces de noms" dans la plupart des autres langages, même s'il s'agit d'espaces de noms auxquels vous pouvez attacher des fonctions et des valeurs. Ils ne sont pas équivalents aux classes, bien que, comme vous l'avez montré, vous puissiez rendre les exportations d'un module égales à celles d'une instance de classe donnée.

Vous pouvez donc créer des singletons en attachant des fonctions et des valeurs directement au module, mais c'est un peu comme créer un singleton en utilisant une classe statique : c'est très peu flexible et ce n'est généralement pas la meilleure pratique. Cependant, la plupart des gens traitent leurs modules comme des "classes statiques", parce que l'architecture correcte d'un système d'injection de dépendances exige beaucoup de réflexion dès le départ, ce qui n'est pas vraiment la norme en JavaScript.


Voici https://gist.github.com/1274607 en ligne :

// EntryPoint.js
define(function () {
    return function EntryPoint(model1, model2) {
        // stuff
    };
});

// Model1.js
define(function () {
    return function Model1() {
        // stuff
    };
});

// Model2.js
define(function () {
    return function Model2(helper) {
        // stuff
    };
});

// Helper.js
define(function () {
    return function Helper() {
        // stuff
    };
});

// composition root, probably your main module
define(function (require) {
    var EntryPoint = require("./EntryPoint");
    var Model1 = require("./Model1");
    var Model2 = require("./Model2");
    var Helper = require("./Helper");

    var entryPoint = new EntryPoint(new Model1(), new Model2(new Helper()));
    entryPoint.start();
});

3voto

unscriptable Points 603

Si vous êtes sérieux au sujet de DI / IOC, vous pourriez être intéressé par wire.js : https://github.com/cujojs/wire

Nous utilisons une combinaison de relocalisation de services (comme le décrit Domenic, mais en utilisant curl.js au lieu de RequireJS) et DI (en utilisant wire.js). La relocalisation de services est très pratique pour utiliser des objets fantaisie dans les harnais de test. DI semble être le meilleur choix pour la plupart des autres cas d'utilisation.

1voto

widged Points 829

Pas un singleton de type getInstance() auto-renforcé, mais un singleton renforcé par le contexte (une instance par "contexte").

Je ne le recommande que pour les objets statiques. Il est parfaitement possible d'avoir un objet statique en tant que module que vous chargez en utilisant les blocs require/define. Vous créez alors une classe avec uniquement des propriétés et des fonctions statiques. Vous avez alors l'équivalent de l'objet Math qui possède des constantes comme PI, E, SQRT et des fonctions comme round(), random(), max(), min(). Idéal pour créer des classes utilitaires qui peuvent être injectées à tout moment.

Au lieu de ça :

define(function() {
    var MyModel = function() {
        this.value = 10;
    }
    return new MyModel();
});

qui crée une instance, utilisez le modèle pour un objet statique (un objet dont les valeurs sont toujours les mêmes car l'objet n'est jamais instancié) :

define(function() {
    return {
       value: 10
    };
});

ou

define(function() {
    var CONSTANT = 10;
    return {
       value: CONSTANT
    };
});

Si vous voulez passer une instance (le résultat de l'utilisation d'un module qui a retourné new MyModel() ;), alors, dans une fonction d'initialisation, passez une variable qui capture l'état / le contexte actuel ou passez l'objet qui contient des informations sur l'état / le contexte que vos modules doivent connaître.

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