JavaScript | Угловой | Шаблон медиатора: тактика устранения циклических зависимостей в AngularJS

Способы реализации принципа инверсии зависимостей в AngularJS

Практический пример: паттерн посредника

Обычно реализация Посредника/Директора подразумевает, что и Директор и Коллега(-и) ссылаются друг на друга. Несложно, если директор отвечает за управление жизненным циклом модулей (MLM):

Директор создает Коллег, передавая себя во время строительства:

var Director = function Director($$) {
    ...

    function init(...) {
        _moduleA = new ModuleA(this);
        _moduleB = new ModuleX(this);
        _moduleX = new ModuleX(this);
    }

    function moduleChanged(ref) {
        if (ref === _moduleXYZ) _moduleC.run(ref.datum);
    }

    return this;
};

Сложная часть: угловая

AngularJS автоматически берет на себя бремя многоуровневого маркетинга, поэтому использовать подход, описанный выше, сложно. Мы могли немного изменить наш метод Director.init:

_moduleX = $dependency;
...
_moduleX.init(this);

Но [моим] коллегам нужна ссылка на директора во время строительства.

Вопрос и объяснение

Хотя мой директор реализован как EDM (событийно-управляемое посредничество), чтобы не нуждаться в DIP, теперь мне нужно, чтобы коллеги ссылались на директора:

var Colleague = function Colleague(director) {
    var thus = this;
    ...

    director.publish(director.CHANNELS.SOME_CHANNEL_NAME, data);

    ...
    return this;
};

Это связано с тем, что директор теперь должен предоставлять авторизацию модуля (MA):

var Director = function Director($$) {
    ...

    function subscribe(channel, handler) {
        var args = Array.prototype.slice.call(arguments, 0);
        $rootScope.$on.apply($rootScope, args);
        return this;
    }

    function publish(channel) {
        var args = Array.prototype.slice.call(arguments, 0);
        if (channel in this.CHANNELS) $rootScope.$broadcast.apply($rootScope, args);
        return this;
    }

    return this;
};

Тем не менее, с точки зрения DIP/DI, практически НЕТ РАЗНИЦЫ между этим подходом и более классическим, впервые упомянутым подходом, основанным на объектных ссылках.

Итак, мой вопрос: как я могу элегантно внедрить DIP и MLM в Angular, не сталкиваясь с проблемами с фреймворком?

Проблемы и возможные обходные пути

  • _colleague.init(this);: ПЛОХО
  • _colleague = Colleague.call($dependency, this): Здорово для одиночек???
  • Разделите управляемое состоянием посредничество и примените интерфейс директора к каждому коллеге.
  • Абстрактный класс посредника [СМ. ниже]

Применить интерфейс директора к каждому коллеге

Импортируйте (DI) класс Mediator в каждого коллегу:

var Mediator = function Mediator($rootScope) {
    ...

    function subscribe(channel, handler) {
        var args = Array.prototype.slice.call(arguments, 0);
        $rootScope.$on.apply($rootScope, args);
        return this;
    }

    function publish(channel) {
        var args = Array.prototype.slice.call(arguments, 0);
        if (channel in this.CHANNELS.SOME_CHANNEL_NAME) $rootScope.$broadcast.apply($rootScope, args);
        return this;
    }

    return this;
};
angular.factory('Mediator', () => Mediator);


var Colleague = function Colleague(Mediator) {
    var thus = this;
    ...

    this.publish(this.CHANNELS.SOME_CHANNEL_NAME, data);

    // export precepts
    Mediator.apply(this);
    this.init = init;
    ...

    return this;
};

Но это может лишить Director возможности посредничества на основе состояния приложения высокого уровня, поскольку это не будет Singleton. НАПРИМЕР:

if (this.state.patientInfoStepComplete) _module.run(ref.datum);

Абстрактный класс посредника

Как и в GoF, среди участников есть посредник и конкретный посредник.


  • Посредник:

    • Defines an Interface for communication with Colleague objects
  • Конкретный посредник:

    • Implements cooperative behavior by coordinating Colleague objects
    • знает и поддерживает своих коллег

Чтобы приблизиться к DIP, можем ли мы взять "this.CHANNELS" и инкапсулировать его в Mediator, но ConcreteMediator все равно потребуется ссылка на него, а Colleague потребуется ссылка на все, что предоставляет оба .CHANNELS и .publish(...).

Вы можете удивиться, зачем мне нужен «Конкретный посредник», если я уже реализую что-то вроде «Абстрактного посредника» с помощью .CHANNELS, который в любом случае может обеспечить маршрутизацию запросов, поскольку его ключи используются коллегами. для абстрагирования значения Channel-Name. Причина в том, что, хотя .CHANNELS может перенаправить один запрос на другой, он может сделать только это -- один единственный запрос -- в то время как .publish(...) может предоставить один запрос -многие обрабатывающие AND работают как промежуточное ПО и другими способами (например, MA).

var _actions = {
    'SOME_CHANNEL_NAME': (e, a, b, c) => {
        $rootScope.emit('a', a);
        $rootScope.emit('b', b);
        $rootScope.emitemit('c', c);
    },
};

function publish() {
    ...
    if (channel in this.CHANNELS) _actions[channel].apply(_actions, args);
    ...
}

Какие-нибудь идеи или шаблоны, которые вы использовали или можете придумать?

Какие-нибудь крутые, нестандартные трюки для регистрации/доступа к зависимостям в Angular?

Есть ли способ в Angular настроить другую зависимость для внедрения в службы во время выполнения?

Вызов angular.constant('contreteMediator', this); во время выполнения внутри Director?

Промежуточное ПО для процесса Provider-Construction?

#PreThanks


person Cody    schedule 17.06.2016    source источник
comment
На мой взгляд, я не думаю, что шаблон Mediator действительно работает для JavaScript. Причина, по которой паттерн посредника работает в C#, заключается в мощной системе интерфейса, чего нельзя сделать в JavaScript из-за отсутствия статической типизации. Вы можете обнаружить, что что-то вроде шаблона стратегии работает немного лучше для шаблона CQRS.   -  person Callum Linington    schedule 18.06.2016
comment
Я бы согласился почти полностью. CQRS? Похоже, что я всегда [усердно] думаю о некоторых вещах, когда внедряю архитектуры с равномерным управлением в JS. Спасибо за ссылку! martinfowler.com/bliki/CQRS.html   -  person Cody    schedule 18.06.2016
comment
Сейчас я пишу пример шаблона CQRS в Angular, чтобы дать вам представление... будет ли он работать или нет... в принципе это легко.... на практике, хорошо...   -  person Callum Linington    schedule 18.06.2016
comment
Прелесть JavaScript в том, что с ним легче справляться с передачей объектов, потому что вам не нужно беспокоиться о статической типизации!! поэтому, если вы сравните с MediatR (Джимми Богард), вам не нужно указывать возвращаемые объекты, а это значит, что вам не нужно об этом думать (хорошо/плохо, вы можете обсудить)   -  person Callum Linington    schedule 18.06.2016
comment
После долгих размышлений тот факт, что JS является языком с динамической типизацией, фактически исключает необходимость использования шаблона для выполнения этих запросов, потому что на самом деле вам не нужен способ обойти ограничения на типизацию возвращаемых значений. Вы также можете придерживаться шаблона использования factory для логики HTTP-запроса и логики просмотра...   -  person Callum Linington    schedule 18.06.2016
comment
@CallumLinington, спасибо за объяснение (я) - пожалуйста, опубликуйте комментарий со скрипкой или чем-то еще с примером этого шаблона Angular CQRS!   -  person Cody    schedule 22.06.2016
comment
Кроме того, не могли бы вы уточнить, можете ли вы придерживаться схемы использования фабрики для http...? Было ли это связано с CQRS или DIP/Mediation/Angular из вопроса? -- спасибо   -  person Cody    schedule 22.06.2016