Problèmes de dépendance circulaire et OOP dans AngularJS

AngularJS + OOP est une fonctionnalité sexy pour utiliser

Bonjour, j'utilise avec succès OOP avec AngularJs depuis un certain temps déjà (commencé par angularjs avec héritage oop en action ), l'approche fournie vous permet de définir vos classes en tant que services angulaires, que vous pouvez ensuite étendre ou hériter de cette manière:

Application.factory('AbstractObject', [function () { var AbstractObject = Class.extend({ virtualMethod: function() { alert("Hello world"); }, abstractMethod: function() { // You may omit abstract definitions, but they make your interface more readable throw new Error("Pure abstract call"); } }); return AbstractObject; // You return class definition instead of it's instance }]); Application.factory('DerivedObject', ['AbstractObject', function (AbstractObject) { var DerivedObject = AbstractObject.extend({ virtualMethod: function() { // Shows two alerts: `Hey!` and `Hello world` alert("Hey!"); this._super(); }, abstractMethod: function() { alert("Now I'm not abstract"); } }); return DerivedObject; }]); 

Plunker: http://plnkr.co/edit/rAtVGAsNYggBhNADMeT

L'utilisation de l'approche décrite vous permet de définir des classes qui s'intègrent parfaitement dans l'infrastructure angulaire. Vous obtenez toutes sortes de fonctionnalités rustiques de deux mondes: OOP et AngularJs. L'injection de dépendance est gratuite pour vos cours, et cela rend vos classes simples, permet de mettre beaucoup de code de contrôleur dans une classe de base qui peut être réutilisé plus tard.

toutefois

Les blocs d'infrastructure AngularJs ont déjà décrit l'approche de la diffusion de ses ailes sur 100%. Le problème se produit lorsque vous essayez de définir des définitions de classe récursives (c.-à-d. Agrégation récursive), disons que vous avez deux définitions de classe comme Blog et Tag

 Application.factory('Blog', ['Tag', function (Tag) { var Blog = Class.extend({ tags: function() { return this.tags; } }); return Blog; }]); Application.factory('Tag', ['Blog', function (Blog) { var Tag = Class.extend({ Blogs: function() { return this.blogs; } }); return Tag; }]); 

Cela ne fonctionnera pas parce que Blog et Tag se Tag eux-mêmes provoquant une dépendance circulaire.

PS

La dernière chose, j'ai trouvé une solution un peu moche qui résout mon problème dans mon cas particulier mais ne fonctionne pas en général et comme je l'ai dit, ce n'est pas joli:

 Application.factory('BlogNamespace', [function () { var Blog = Class.extend({ tags: function() { return this.tags; } }); var Tag = Class.extend({ Blogs: function() { return this.blogs; } }); return { Tag: Tag, Blog: Blog }; }]); 

Question

Le correctif ci-dessus ne fonctionnera pas car les espaces de noms peuvent également faire l'objet d'une dépendance circulaire. Cela signifie que ce n'est pas une solution au problème décrit, mais plutôt un problème de niveau plus grave maintenant.

Des suggestions sur la façon dont il est possible de résoudre le problème décrit dans le cas général?

Une dépendance circulaire est toujours le signe du mélange des préoccupations , ce qui est vraiment dommage. Miško Hevery, l'un des auteurs d'AngularJS, explique une belle solution sur son blog génial . En bref, vous avez probablement un troisième service caché quelque part, qui est la seule partie de votre code vraiment nécessaire par les deux autres.

Je réponds à ma propre question simplement parce que j'ai trouvé un moyen technique de résoudre le problème dont j'ai parlé initialement. Mais avant cela, je vous encourage vivement à utiliser la suggestion de Blackhole, car elle permet de résoudre un ensemble plus large de problèmes qui sont habituellement causés par une mauvaise architecture. S'il vous plaît préférez d'abord utiliser son approche, et revenez à l'heure actuelle si vous savez ce que vous faites.

Alors voici:

Vous pouvez utiliser le service $injector et injecter les définitions requises au moment de l'exécution, ce qui est légal du point de vue technique, mais encore selon cette publication (difficile à imaginer qu'il soit écrit en 2008), c'est comme une magie noire. Et cela vous rappellera:

 Application.factory('Blog', ['$injector', function ($injector) { var Tag = $injector.get('Tag'); // Here is your tag ... }]); Application.factory('Tag', ['Blog', function (Blog) { ... }]); 

modifier

Il s'est avéré que l'approche actuelle est un exemple de modèle de localisateur de service, qui est IoC Antipattern.

DERNI RESORT: NON ENCOURAGÉ

Dans mon cas, la meilleure façon de contourner un problème de dépendance circulaire comme celui-ci en angulaire, est de déclencher des appels de fonctions via $rootScope -radiances . L'autre service peut alors écouter cette diffusion et réagir avec l'appel de fonction désiré. Ce n'est peut-être pas la solution la plus élégante, mais dans certains cas où l'interaction entre les services est essentiellement une directionnelle, c'est peut-être une alternative raisonnable. (Notez que cela permet également de renvoyer les valeurs de retour à la fonction de diffusion via les rappels uniquement)


Un pseudo exemple de ceci serait:

 angular.module('myApp').factory('service1', ["$rootScope", function($rootScope) { function func1() { // do something } $rootScope.$broadcast("callFunc2"); // calls func2 from service 1 return { func1: func1 } } ]); angular.module('myApp').factory('service2', ["service1", "$rootScope", function(service1, $rootScope) { function func2() { // do something } service1.func1(); // calls func1 from service 2 $rootScope.on("callFunc2", func2); } ]);