AngularJS中循环依赖和OOP的问题

时间:2013-10-13 10:37:58

标签: javascript oop angularjs

AngularJS + OOP使用

是一种性感的功能

嗨,我已成功使用OOP和AngularJs已经有一段时间了(首先从angularjs with oop inheritance in action开始),提供的方法允许您将类定义为角度服务,稍后您可以扩展或继承这些类:

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/rAtVGAsNYggBhNADMeoT

使用所描述的方法,您可以定义精美集成到角度基础结构中的类。你可以从两个世界中获得各种漂亮的功能--OOP和AngularJs。依赖注入对于您的类是免费的,它使您的类变得简单,允许将大量样板控制器代码放入某个基类中,以后可以重用。

然而

先前描述的AngularJs基础设施模块在所有100%上展开它的翅膀。当您尝试定义递归类定义(即递归聚合)时会出现问题,例如,您有两个类定义,如BlogTag

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

它不起作用,因为BlogTag都是自引用本身导致循环依赖。

P.S

最后一件事,我发现有点丑陋的解决方案在我的具体案例中解决了我的问题,但一般不起作用,正如我所说,它并不漂亮:

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

问题

上述修复不起作用,因为名称空间也可能是循环依赖的主题。这意味着它不是所描述问题的解决方案,而是现在更深层次的问题。

关于如何解决一般情况下描述的问题的任何建议?

3 个答案:

答案 0 :(得分:66)

循环依赖始终是混合关注的标志,这是一件非常糟糕的事情。 AngularJS的作者之一MiškoHevery解释了一个很好的解决方案on his awesome blog。简而言之,您可能在某处隐藏了第三个服务,这是您的代码中唯一真正需要的其他部分。

答案 1 :(得分:53)

我正在回答我自己的问题,因为我找到了解决我最初发布的问题的技术方法。但在此之前,我强烈建议您使用Blackhole的建议,因为它允许解决通常由不良架构引起的更广泛的问题。请首先使用他的方法,如果您知道自己在做什么,请返回当前的方法。

所以这里是:

您可以使用$injector服务并在运行时注入所需的定义,从技术角度来看这是合法的,但再次根据this帖子(很难想象它是在2008年编写的) ),这就像一个黑魔法,这样做会让你反感:

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

    ...    
}]);

Application.factory('Tag', ['Blog', function (Blog) {
    ...
}]);

修改

事实证明,当前的方法是Service Locator模式的一个例子,它是IoC Antipattern。

答案 2 :(得分:1)

最后的度假村:没有鼓励

在我的情况下,以角度方式绕过这样的循环依赖问题的最佳方法是通过$rootScope触发函数调用 - 广播。然后,另一个服务可以收听此广播并对所需的函数调用做出反应。它可能不是最优雅的解决方案,但在某些情况下,服务之间的交互主要是单向的,它可能是一个合理的替代方案。 (请注意,这也允许返回值仅通过回调传递回广播功能)

这样的一个伪示例是:

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