javascript and Injection de dépendance avec RequireJS



requirejs import js file (3)

Combien puis-je étirer 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 dans un getInstance () - tapez un singleton, mais un singleton à contexte (une instance par "contexte"). Je voudrais faire quelque chose comme ...

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

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

J'ai réussi à faire ce travail en rendant le module mymodel comme ceci:

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

Ce type d'utilisation est-il prévu et courant ou est-ce que j'abuse de RequireJS? Existe-t-il un moyen plus approprié de réaliser une injection de dépendance avec RequireJS? Merci de votre aide. J'essaie toujours de comprendre cela.


Answer #1

Pas un singleton dans un getInstance () - tapez singleton, mais un singleton imposé par le contexte (une instance par "contexte").

Je le recommanderais uniquement pour les objets statiques. C'est parfaitement bien d'avoir un objet statique en tant que module que vous chargez en utilisant les blocs require / define. Vous créez ensuite une classe avec uniquement des propriétés et des fonctions statiques. Vous avez alors l'équivalent de l'objet mathématique qui a des constantes telles que PI, E, SQRT et des fonctions telles que round (), random (), max (), min (). Idéal pour créer des classes d’utilitaires pouvant être injectées à tout moment.

Au lieu de cela:

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

Pour créer une instance, utilisez le modèle d’un objet statique (un modèle dans lequel les valeurs sont toujours identiques à celles de l’objet à instancier):

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

ou

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

Si vous souhaitez transmettre une instance (résultat de l'utilisation d'un module renvoyant un nouveau MyModel ();), puis, dans une fonction d'initialisation, transmettez une variable qui capture l'état / le contexte actuel ou transmettez l'objet contenant des informations sur Etat / contexte que vos modules doivent connaître.


Answer #2

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

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


Answer #3

Ce n'est pas réellement une injection de dépendance, mais un emplacement de service: vos autres modules demandent une "classe" par une chaîne "clé" et en récupèrent une instance à laquelle le "localisateur de service" (dans le cas présent, RequireJS) a été câblé. prévoir pour eux.

L'injection de dépendance impliquerait le renvoi du constructeur MyModel , c'est-à-dire le return MyModel , puis dans une racine de composition centrale en injectant une instance de MyModel dans d'autres instances. J'ai rassemblé un exemple de la façon dont cela fonctionne ici: https://gist.github.com/1274607 (également cité ci-dessous)

De cette manière, la racine de la composition détermine si une instance unique de MyModel (c’est-à-dire en faire un objet singleton), ou une nouvelle pour chaque classe qui en a besoin (une instance portée), ou quelque chose entre les deux. Cette logique n'appartient ni à la définition de MyModel, ni aux classes qui en demandent une instance.

(Remarque: bien que je ne l'utilise pas encore, wire.js est un conteneur d'injection de dépendance à 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 plutôt que de renvoyer 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 manque peut-être, c'est que les modules sont équivalents aux "espaces de noms" dans la plupart des autres langues, bien que vous puissiez y associer des fonctions et des valeurs. (Ils ressemblent donc davantage à Python qu'à Java ou C #.) Ils ne sont pas équivalents à des classes, bien que, comme vous l'avez montré, vous pouvez 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 hautement inflexible et généralement pas une bonne pratique. Cependant, la plupart des gens traitent leurs modules comme des "classes statiques", car concevoir correctement un système pour l'injection de dépendances nécessite 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 inline:

// 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();
});




requirejs