vendredi 17 juin 2016

JavaScript | Angular | Mediator Pattern: Tactics for Obviating Circular Dependencies In AngularJS

Ways to Implement Dependency Inversion Principle in AngularJS

Case Study: The Mediator Pattern

Normally, implementing a Mediator/Director entails both Director and Colleague(s) having a reference to each other. Not difficult when Director is responsible for Module-Lifecycle Management (MLM):

Director creates Colleagues while passing itself in during construction:

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

The Tricky Part: Angular

AngularJS automatically takes on the burden of MLM, so its tough to use the approach above. We could modify our Director.init method slightly:

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

But [my] Colleagues need a reference to Director at construction-time.

Question and Explanation

While my Director is implemented as EDM (Event-Driven Mediation) to run away from needing DIP, I now need Colleagues to reference Director:

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

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

    ...
    return this;
};

Because Director must now provide Module Authorization (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.SOME_CHANNEL_NAME) $rootScope.$broadcast.apply($rootScope, args);
        return this;
    }

    return this;
};

That said, there's virtually NO-DIFFERENCE -- as far as DIP/DI is concerned -- between this approach and the more Classical, object-reference driven approach first mentioned.

So my question is, what are different ways that I can elegantly impose DIP and MLM in Angular without running into problems with the framework?

Concerns & Potential Workarounds

  • _colleague.init(this);: BAD
  • _colleague = Colleague.call($dependency, this): Healthy for Singletons???
  • Mediator Abstract Class [SEE below]

Abstract Mediator Class

As in GoF, the Participants include a Mediator and a ConcreteMediator.


  • Mediator:

    • Defines an Interface for communication with Colleague objects
  • ConcreteMediator:

    • Implements cooperative behavior by coordinating Colleague objects
    • knows and maintains its colleagues

To follow a little closer to DIP, can we take "this.CHANNELS" and apply it to Mediator, but ConcreteMediator would still need a reference to it and Colleague would need a reference to whatever is providing both .CHANNELS and .publish(...).

Any ideas or patterns you've used or can think of?

#PreThanks

Aucun commentaire:

Enregistrer un commentaire