AngularJS中循环依赖和OOP的问题

Lu4*_*Lu4 59 javascript oop angularjs

AngularJS + OOP是一种性感的功能

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

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;
}]);
Run Code Online (Sandbox Code Playgroud)

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;
}]);
Run Code Online (Sandbox Code Playgroud)

它不会因为两者的工作BlogTag是自引用本身造成循环依赖.

PS

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

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
    };
}]);
Run Code Online (Sandbox Code Playgroud)

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

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

Bla*_*ole 67

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


Lu4*_*Lu4 54

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

所以这里:

您可以$injector在运行时使用服务并注入所需的定义,从技术角度来看这是合法的,但是根据这篇文章(很难想象它是在2008年编写),这就像一个黑魔法,做到这一点,它会打击你:

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

    ...    
}]);

Application.factory('Tag', ['Blog', function (Blog) {
    ...
}]);
Run Code Online (Sandbox Code Playgroud)

编辑

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

  • 加入黑暗的一面:) (27认同)
  • 这个答案节省了我的一天!+1 (4认同)
  • @whitebox它不适用于单元测试,但如果你在模块首次初始化后使用它(例如在模块中的函数内使用它),它可以正常工作.我认为Lu4在撰写答案时简化了案例,以便缩短. (3认同)
  • @RodrigoQuesada你的建议是一个真正年轻而充满激情的开发者的建议:)你真的很酷,你受教条和范式,这意味着你选择自己的方式.然而对我来说它看起来很有趣,因为我已经太老了,无法遵循它.请不要把我的话当作侮辱,而是把它们视为经验的声音,是你未来的声音.我发现很多理由担心你的主张.其中之一是我知道它们是由许多无辜的开发者的血液写成的.而且我不需要每个人都同意这个事实,我只需要聪明的家伙 (2认同)
  • 这不起作用,$ injector实际上是报告从http://jsfiddle.net/iamwhitebox/1z2kua70/开始的周期性依赖 (2认同)