2009-03-06 6 views
16

Je suis actuellement confronté à une énigme: Quelle est la bonne façon de relier 2 objets javascript?Quelle est la bonne façon de relier 2 objets javascript?

Imaginez une application comme un éditeur de texte avec plusieurs fichiers différents. J'ai une page HTML qui représente la vue pour le cahier. J'ai un fichier notebook.js qui contient des définitions de classe pour NotebookController et Notebook View.

Objet NotebookControler responsable de l'exécution de la logique métier sur le portable, par exemple «Enregistrer le bloc-notes», «Charger le bloc-notes», «Nouveau bloc-notes». NotebookView est responsable de la gestion du code HTML utilisé pour la présentation. Il fait des choses de bas niveau comme "get/set notebook body" "obtenir/définir le nom du portable." Il écoute également les événements DOM (onClick) et déclenche les événements professionnels (saveNotebook). C'est ma tentative pour le modèle Passive View. Je souhaite que mon code JavaScript côté client soit orienté objet, séparé des problèmes et testable à l'unité. Je veux tester NotebookController avec un faux NotebookView et vice versa. Cela signifie que je ne peux pas simplement instancier un NotebookView dans le NotebookController. Alors que je

  • Mettez un peu de logique dans mes notebook.js que les fils les 2 ensemble
  • ont une fonction globale dans mon application qui sait instancier un de chacun et de les câbler
  • Utilisation Dependency Injection, Soit d'origine locale ou quelque chose comme SquirrelIoc

En Java, le choix est naturel: utilisez Spring. Mais cela ne semble pas très JavaScript. Quelle est la bonne chose à faire?

+0

Peut-être que je manque quelque chose, mais pouvez-vous pas atteindre testabilité en faisant juste que vous utilisez l'agrégation au lieu de composition? –

+0

C'est l'idée. Mais la question est vraiment quelle est la bonne façon de câbler cette agrégation d'objets? –

Répondre

0

Je vais essayer de faire un coup de poignard, mais ce sera un peu difficile sans voir de code réel. Personnellement, je n'ai jamais vu quelqu'un faire une tentative si spécifique à (M) VC avec JavaScript ou IoC d'ailleurs.

Tout d'abord, avec quoi allez-vous tester? Si ce n'est déjà fait, consultez le YUI Test video qui contient de bonnes informations sur les tests unitaires avec javascript.

En second lieu, quand vous dites « la meilleure façon de câbler que l'agrégation » je ne serais probablement pas tout comme un setter w/contrôleur

// Production 
var cont = new NotebookController(); 
cont.setView(new NotebookView()); 

// Testing the View 
var cont = new NotebookController(); 
cont.setView(new MockNotebookView()); 

// Testing the Controller 
var cont = new MockNotebookController(); 
cont.setView(new NotebookView()); 

// Testing both 
var cont = new MockNotebookController(); 
cont.setView(new MockNotebookView()); 

Mais cela fait une grande hypothèse sur la façon dont vous avez conçu votre contrôleur et voir les objets déjà.

2

Je dirais, il suffit de les câbler:

function wireTogether() { 
    var v = new View(); 
    var c = new Controller(); 
    c.setView(v); 
} 

Mais bien sûr une autre question soulève - Comment tester la fonction wireTogether()?

Heureusement, JavaScript est un langage vraiment dynamique, de sorte que vous pouvez simplement affecter de nouvelles valeurs à l'affichage et le contrôleur:

var ok = false; 

View.prototype.isOurMock = true; 
Controller.prototype.setView = function(v) { 
    ok = v.isOurMock; 
} 

wireTogether(); 

alert(ok ? "Test passed" : "Test failed"); 
3

injection de dépendance est probablement votre meilleur pari. Comparé à Java, certains aspects de ce sont plus faciles à faire dans le code JS, puisque vous pouvez passer un objet rempli de callbacks dans votre NotebookController. D'autres aspects sont plus difficiles, car vous n'avez pas l'analyse de code statique pour formaliser l'interface entre eux.

3

Merci pour la perspicacité.J'ai fini par écrire un simple utilitaire d'injection de dépendance JavaScript. Après avoir discuté pendant un certain temps et vos commentaires, il me est survenue à cette DI était vraiment la bonne réponse parce que:

  1. Il sépare totalement les préoccupations de câblage de la logique métier tout en gardant la logique de câblage à proximité des choses étant câblé .
  2. Il m'a permis de fournir génériquement un rappel "vous êtes tous branchés" sur mes objets afin que je puisse faire une initialisation en 3 phases: instancier tout, tout câbler, appeler les rappels de tout le monde et leur dire qu'ils sont branchés .
  3. Il était facile de vérifier l'absence de dépendance.

Voici donc l'utilitaire DI:

var Dependency = function(_name, _instance, _dependencyMap) { 
    this.name = _name; 
    this.instance = _instance; 
    this.dependencyMap = _dependencyMap; 
} 

Dependency.prototype.toString = function() { 
    return this.name; 
} 

CONCORD.dependencyinjection = {}; 

CONCORD.dependencyinjection.Context = function() { 
    this.registry = {}; 
} 

CONCORD.dependencyinjection.Context.prototype = { 
    register : function(name, instance, dependencyMap) { 
     this.registry[name] = new Dependency(name, instance, dependencyMap); 
    }, 
    get : function(name) { 
     var dependency = this.registry[name]; 
     return dependency != null ? dependency.instance : null; 
    }, 

    init : function() { 
     YAHOO.log("Initializing Dependency Injection","info","CONCORD.dependencyinjection.Context"); 
     var registryKey; 
     var dependencyKey; 
     var dependency; 
     var afterDependenciesSet = []; 
     for (registryKey in this.registry) { 
      dependency = this.registry[registryKey]; 
      YAHOO.log("Initializing " + dependency.name,"debug","CONCORD.dependencyinjection.Context"); 

      for(dependencyKey in dependency.dependencyMap) { 
       var name = dependency.dependencyMap[dependencyKey]; 
       var instance = this.get(name); 
       if(instance == null) { 
        throw "Unsatisfied Dependency: "+dependency+"."+dependencyKey+" could not find instance for "+name; 
       } 
       dependency.instance[dependencyKey] = instance; 
      } 

      if(typeof dependency.instance['afterDependenciesSet'] != 'undefined') { 
       afterDependenciesSet.push(dependency); 
      } 
     } 

     var i; 
     for(i = 0; i < afterDependenciesSet.length; i++) { 
      afterDependenciesSet[i].instance.afterDependenciesSet(); 
     } 
    } 

} 
1

J'ai une inversion de la bibliothèque de contrôle pour javascript, je suis assez content. https://github.com/fschwiet/jsfioc. Il prend également en charge les événements, donc si vous voulez avoir un événement de démarrage c'est très bien. Il pourrait utiliser plus de documentation ...

http://github.com/fschwiet/jsfioc

Une autre (nouvelle?) Option, qui a une meilleure documentation et de soutien, est requireJS (http://requirejs.org/).

Questions connexes