Question

// Main class
function App() {
    this.task = new Task(this); // pass the instance of this class to Task so 
                                // it has access to doSomething
}

App.prototype.doSomething = function () {
    alert("I do something that Task() needs to be able to do!");
};

function Task(app) {
    // This class needs access to App()'s doSomething method
    this.appInstance = app;
    this.appInstance.doSomething(); // Great, now Task can call the method
}

var app = new App();

The aim of the code above is to give Task access to one of App's methods called doSomething. The code is the current way I'd go about it and I'm posting this to see if it's the best way...

To give Task access I simply pass the whole instance of App, is this efficient or is there a better way to go about it? Is the code above general practice in going about doing something like this?

Was it helpful?

Solution

Yes, what you have is fine. It is a circular dependency, however because of JavaScript's dynamic nature there aren't really any issues.

Another way you could reference App from Task would be a Singleton pattern or something similar, but that would probably be harder to test.

OTHER TIPS

jsFiddle Demo

Generally bind would be used in this scenario assuming that the Task "class" didn't also setup other facilities which were not shown here.

Bind allows for the context to be provided for a function. This could be done in app's constructor. At which point only a function task would be required to call "someMethod".

function task(){
    return this["someMethod"]();
}

function App(){
    task.bind(this)();
}

App.prototype.someMethod = function(){
    alert("Task needed access to this");
};

var a = new App();

However, if task must be a "class", and have other responsibilities then the prototype function could be shared.

function Task(){}
function App(){}

App.prototype.someMethod = Task.prototype.someMethod = function(){
    alert("Task needed access to this");
};

var a = new App();
a.task();//->"Task needed access to this"
var t = new Task();
t.someMethod();//->"Task needed access to this"

Your app instances and task instances are tightly bound. App instances have tasks and this can be fine.

A design of loosely coupled objects is more flexible and easier to extend but more complicated to initially create. One such pattern is using a mediator/publish subscriber and have app raise an event/publish message any other object function can listen to this and take action on the event.

For example: your app creates an Ajax instance and when that instance is done it raises some event (fetchedData for example). A listener could be DomDependent.updateView function but later you may want to add/remove/change the order of tasks to do after data is fetched. This can all be configured in a app.init function or per procedure in a controller that kicks of certain procedures (like log in, search, ...).

Instead of creating a whole bunch of specific functions in Ajax (fetchUserPrefs, login, search, ...) you can create one general function and have the controller add listeners or pass the next event when fetchData is complete to run the correct next function.

Here is some pseudo code:

var app = {
  init:function(){
    mediator.add("updateLogin",domDependent.updateView);
    mediator.add("updateLogin",app.loadUserPrefs);
    mediator.add("failLogin",domDependent.updateView);
  },
  login: function(){
    mediator.trigger("loadingSometing",{type:"login"});
    ajax.fetch({
      onComplete:"updateLogin",//what listens to updateLogin you decided in init
      onFail:"failLogin",
      loginDetails:domDependent.getLogin(),
      url:settings.loginUrl,
      type:"post"
    });
  }
}

var ajax = {
  fetch:function(data){
    data = data || {};
    //simple check for onComplete, it's mandatory
    var complete = data.onComplete || app.raiseError("ajax.fetch needs onComplete");
    //other code to validate data and making ajax request
    onSuccess:function(resp){
      //mutate data object as the mediator will pass it to
      //  whatever other function is called next
      //  you don't hard code domDependent.updateView and
      //  app.loadUserPrefs because fetch can be used generally and
      //  success may have to do completely different things after its done
      //  and you want to define procedures in init, not all over your code
      data.response=resp;
      //trigger event to do whatever needs to be done next
      mediator.trigger(complete,data);
    }
  }
}

As you can see it gets complicated and maybe doesn't look like code you're used to but it's highly configurable.

I may have misunderstood the advantages of the mediator pattern to loose couple and if so please comment. I use it to:

  1. Make methods more general instead of copying a lot of logic only because what to do after it's done is different. In fetch the ajax object just fetches, this would be the same for login or getting user preferences, the only thing different is what function to call next/on error when it's done.

  2. A procedure like login involves multiple functions in multiple objects if this function chain hard code what to do next once a particular function is done your procedure of login is defined all over your code. When defining it in init/config you can easily change the order or add/remove functions in the chain.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top