2012-04-17 4 views
12

Эта проблема лучше иллюстрируется примером. Я буду использовать 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, я мог бы использовать рамки Dependency Injection, чтобы сделать это для меня, и мой код будет выглядеть следующим образом:

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, я знаю ... но на самом деле рамки DI делает всю проводку, поэтому, по крайней мере, эта часть проблемы проще.

Теперь вернемся к нашему веб-приложению и Javascript. Даже если мне удастся всегда вызывать onMustFetch с правом ajaxRequest ссылка (ведь в этом случае это не так сложно сделать), должен быть более простой способ. Когда мой код растет, зависимости увеличиваются. Я могу себе представить, передавая ссылку на ajaxRequest вокруг, но что делать, когда у меня есть securityService, а browserService, а eventBusService, и т.д., и т.д., и т.д.?

Теперь реальный вопрос здесь: Как lisp как языки решают эту проблему управления зависимостями? (Мне кажется, что зависимости должны постоянно меняться во всем приложении, но я уверен, что должен быть лучший способ ...)

+2

Lisp-подобные языки отличаются друг от друга. У них разные системы объектов и т. Д. Этот вопрос действительно касается Javascript: как реализовать шаблон дизайна Java (Injection Dependency) в Javascript. Я удаляю теги вне темы [clojure] [lisp] и [схема]. – Kaz

+4

Речь идет не о javascript. Javascript просто бывает таким, каким я знаком, чтобы выразить эту проблему. Я заинтересован в том, как языки lisp имеют дело с проблемой, поэтому я бы очень хотел, чтобы вы вернули теги. Не стесняйтесь перефразировать вопрос таким образом, который имеет смысл для Lisp, Clojure или Scheme. – RobotFoo

ответ

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 не так много отражения, поэтому создайте соглашение, чтобы сообщить системе, что нужно. В этом случае я использовал массив под названием «зависимости», но вы могли бы использовать любую конструкцию, которая вам нравится.

+0

Да, я мог бы использовать технику, как я бы использовал в OO. Но мой вопрос - и интерес - это то, как функциональные языки, в частности языки lisp, управляют зависимостями. Я знаю, что, возможно, это было недостаточно ясно в моем вопросе, но прочитал мой комментарий к вопросу, и вы поймете, почему. Спасибо, хотя ... – RobotFoo

+0

В этом случае я думаю, что исходный комментарий очень важен. Или вы спрашиваете, как управлять зависимостями без, скажем, концепции глобалов или фабрики? Зачем вам это делать? Инструменты, которые у вас есть для создания таких конструкций, очень специфичны для языка. Без какой-либо глобальной конструкции, тогда вы уже ответили на свой вопрос, вы будете передавать вещи вокруг. –

+0

Действительно, варианты, которые я до сих пор получаю, - это инъекция зависимостей, общие глобальные переменные, фабрика или передача материала. Я пытался выяснить, что обычно делается на Lisp, как языки. Итак, возможно, вы правы, может быть, нет другого ответа на мой вопрос в контексте, который я ставил. – RobotFoo

5

Обычно это делается с помощью закрытий. В JS вы можете сделать:

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

var userGrid = buildUserGrid(ajaxRequest); 
userGrid.onMustFetch(); 
Смежные вопросы