This problem is better illustrated with an example. I'll use Javascript (actually Coffeescript for syntax sake), but just because Javascript is just another LISP, right?
So, suppose I'm writing a web app which does (obviously) ajax requests. I implement a function to handle that:
ajaxRequest = (url, params, callback) ->
# implementation goes here
Now, suppose I have a grid that fetches data from the server. Somewhere in my code I must do something like this:
userGrid.onMustFetch = ->
ajaxRequest '/fetch/users', { surname: 'MacGyver' }, (data) ->
# fill grid with data
What exactly is the problem here? If I want to test the implementation of onMustFetch, I will not be able to do so, because inside onMustFetch, a dependency is being called, and the test environment cannot control the dependency.
To solve this problem, I inject the dependency into the function I want to test. That means changing onMustFetch to this:
userGrid.onMustFetch = (ajaxRequest) ->
ajaxRequest '/fetch/users', { surname: 'MacGyver' }, (data) ->
# fill grid with data
Now the test code can pass a mock of ajaxRequest to onMustFetch and successfully test the behavior.
Wunderbar, right? Wrong! Now I have a second problem, the problem of having to bind the right instance of ajaxRequest to the right instance of onMustFetch.
In a language like Java, I could use a Dependency Injection framework to do this for me, and my code would look like this:
class UserGrid {
private AjaxService ajaxService;
@Inject
public UserGrid(AjaxService ajaxService) {
this.ajaxService = ajaxService;
}
public void onMustFetch() {
HashMap<String, String> params = new HashMap<String, String>();
params.put("surname", "MacGyver");
ajaxService.request("/fetch/users", params, new AjaxCallback(data) {
// fill grid with data
});
}
}
Creepy, I know... but actually the DI framework does all the wiring, so at least that part of the problem is easier.
Now back to our web app and to Javascript. Even if I successfully manage to always invoke onMustFetch with the right ajaxRequest reference (after all in this case that is not so hard to do), there must be an easier way. When my code grows, the dependencies increase. I can imagine passing a reference of ajaxRequest around, but what about when I have a securityService, a browserService, a eventBusService, etc, etc, etc.?
Now the real question here: How do lisp like languages solve this problem of managing dependencies? (It seems to me the dependencies must keep being passed around all over the application, but I'm sure there must be a better way...)
This is typically done using closures/currying. You pass in your dependencies as parameters. In JS you could do:
buildUserGrid = function(dependency){
return {
onMustFetch = function(){
depenency.doSomething();
},
doSomethingElse = function(){
dependency.doSomethingElse();
}
}
}
var userGrid = buildUserGrid(ajaxRequest);
userGrid.onMustFetch();
In Javascript I don't know why you couldn't use techniques similar to any OO language. A very basic implementation in JS (sorry, I don't know Coffescript)
// expects a function
var UserGrid = function(ajaxService) {
this.dependencies = ["ajaxService"];
// will be overwritten by the DI service, but can also be
// assigned manually as in java
this.ajaxService = ajaxService;
};
UserGrid.prototype.onMustFetch=function() {
var callback = function() { ... }
this.ajaxService('/fetch/users',{ surname: 'MacGyver' }, callback);
};
var diController = {
create: function(constr) {
var obj = new constr();
// just look at obj.dependencies to see what should be assigned, and map
// the implemenations as properties of obj. this could be
// as simple as a switch or a direct mapping of names to object types
// ... assign implementations to obj
return obj;
}
};
Create:
var userGrid = diController.create(UserGrid);
So diController
does the same thing as your java dependency injector. In java it can just figure out what type of object is needed using reflection. There's not much reflecting to be done in Javascript, so create a convention to tell the system what is needed. In this case I used an array called "dependencies" but you could use any construct you like.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With