14

用一个例子可以更好地说明这个问题。我将使用 Javascript(实际上是为了语法而使用 Coffeescript),但只是因为 Javascript 只是另一个LISP,对吗?

所以,假设我正在编写一个执行(显然)ajax 请求的网络应用程序。我实现了一个函数来处理它:

ajaxRequest = (url, params, callback) ->
    # implementation goes here

现在,假设我有一个从服务器获取数据的网格。在我的代码的某个地方,我必须做这样的事情:

userGrid.onMustFetch = ->
    ajaxRequest '/fetch/users', { surname: 'MacGyver' }, (data) ->
        # fill grid with data

这里到底有什么问题?如果我想测试onMustFetch的实现,我将无法这样做,因为在onMustFetch内部,正在调用依赖项,并且测试环境无法控制依赖项。

为了解决这个问题,我将依赖注入到我要测试的函数中。这意味着将onMustFetch更改为:

userGrid.onMustFetch = (ajaxRequest) ->
    ajaxRequest '/fetch/users', { surname: 'MacGyver' }, (data) ->
        # fill grid with data

现在测试代码可以将ajaxRequest的模拟传递给onMustFetch并成功测试行为。

奇妙吧,对吧?错误的!现在我有第二个问题,必须将 ajaxRequest 的正确实例绑定到onMustFetch的正确实例的问题

在像 Java 这样的语言中,我可以使用依赖注入框架来为我做这件事,我的代码看起来像这样:

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

}

令人毛骨悚然,我知道...但实际上 DI 框架完成了所有的布线,所以至少这部分问题更容易。

现在回到我们的 Web 应用程序和 Javascript。即使我成功地设法始终使用正确的ajaxRequest引用调用onMustFetch(毕竟在这种情况下并不难做到),也必须有一种更简单的方法。当我的代码增长时,依赖关系会增加。我可以想象传递一个ajaxRequest的引用,但是当我有一个securityService、一个browserService、一个eventBusService等等等等时呢?

现在真正的问题在这里:类似 lisp 的语言如何解决管理依赖项的问题?(在我看来,依赖项必须在整个应用程序中不断传递,但我确信必须有更好的方法......)

4

2 回答 2

6

这通常使用闭包/柯里化来完成。您将依赖项作为参数传递。在 JS 中你可以这样做:

buildUserGrid = function(dependency){
    return {
        onMustFetch = function(){
            depenency.doSomething();
        },
        doSomethingElse = function(){
            dependency.doSomethingElse();
        }
    }
}

var userGrid = buildUserGrid(ajaxRequest);
userGrid.onMustFetch();
于 2012-09-17T06:28:20.543 回答
1

在 Javascript 中,我不知道为什么你不能使用类似于任何 OO 语言的技术。一个非常基本的 JS 实现(抱歉,我不知道 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;
    }
};

创造:

var userGrid = diController.create(UserGrid);

diController与您的 java 依赖注入器相同的事情也是如此。在java中,它可以使用反射来确定需要什么类型的对象。在 Javascript 中没有太多的反射要做,所以创建一个约定来告诉系统需要什么。在这种情况下,我使用了一个名为“dependencies”的数组,但您可以使用任何您喜欢的构造。

于 2012-04-17T20:01:47.183 回答