Подтвердить что ты не робот

Проблемы с круговой зависимостью и ООП в AngularJS

AngularJS + OOP - своего рода сексуальная функция для использования

Привет, я успешно использую OOP с AngularJs уже некоторое время (сначала начал с angularjs с наследованием oop в действии), предоставленный подход позволяет вам определить ваш классы как службы angular, которые вы можете впоследствии расширить или наследовать от этого:

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

с использованием описанного подхода дает вам возможность определять классы, которые прекрасно интегрируются в инфраструктуру angular. Вы получаете всевозможные отличные функции из двух миров - ООП и AngularJs. Инъекционная инъекция бесплатна для ваших классов, и это делает ваши классы простыми, позволяет помещать много кода контроллера шаблона в некоторый базовый класс, который может быть впоследствии использован повторно.

Однако

Инфраструктура AngularJs блокирует ранее описанный подход от распространения его крыльев на всех 100%. Проблема возникает, когда вы пытаетесь определить рекурсивные определения классов (например, рекурсивную агрегацию), например, у вас есть два определения класса, такие как Blog и 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;
}]);

Это не сработает, потому что оба Blog и Tag сами ссылаются на себя, вызывая круговую зависимость.

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

Вопрос

Вышеупомянутое исправление не будет работать, поскольку пространства имен также могут быть предметом круговой зависимости. Это означает, что это не решение описанной проблемы, а проблема на уровне ниже уровня.

Любые предложения о том, как можно решить описанную проблему в общем случае?

4b9b3361

Ответ 1

Круговая зависимость всегда является признаком <сильного > смешения проблем, что очень плохо. Мишко Хевери, один из авторов AngularJS, объясняет приятное решение в своем удивительном блоге. Короче говоря, у вас, вероятно, есть какая-то третья служба, которая является единственной частью вашего кода, которая действительно нужна двум другим.

Ответ 2

Я отвечаю на свой вопрос только потому, что нашел технический способ решения проблемы, о которой я уже писал. Но до этого я настоятельно рекомендую вам использовать предложение Blackhole, поскольку оно позволяет решать более широкий набор проблем, которые обычно вызывают плохая архитектура. Пожалуйста, сначала используйте его подход и вернитесь к текущему, если вы знаете, что вы делаете.

Итак, вот:

Вы можете использовать службу $injector и вводить требуемые определения во время выполнения, что является законным с технической точки зрения, но в соответствии с this post (трудно представить, что это написано в 2008 году), это похоже на черную магию, сделайте это, и она поразит вас:

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

    ...    
}]);

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

Изменить

Оказалось, что текущий подход является примером шаблона Locator Service, который является IoC Antipattern.

Ответ 3

ПОСЛЕДНИЙ КУРОРТ: НЕ ОБЕСПЕЧЕН

В моем случае лучший способ обойти проблему с круговой зависимостью, подобную этой в angular, - это вызвать функции-вызовы через $rootScope -broadcasts. Затем другая служба может прослушивать эту трансляцию и реагировать на желаемый вызов функции. Это может быть не самое элегантное решение, но в некоторых случаях, когда взаимодействие между службами в основном однонаправлено, это может быть разумной альтернативой. (обратите внимание, что это также позволяет возвращать возвращаемые значения обратно в функцию вещания только через обратные вызовы)


Псевдо-пример этого:

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