Способы реализации принципа инверсии зависимостей в 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