Как правильно связать вместе 2 объекта javascript? - PullRequest
16 голосов
/ 06 марта 2009

В настоящее время я сталкиваюсь с загадкой: как правильно соединить 2 объекта javascript?

Представьте себе приложение, похожее на текстовый редактор с несколькими различными файлами. У меня есть HTML-страница, которая представляет представление для ноутбука. У меня есть файл notebook.js, который содержит определения классов для NotebookController и Notebook View.

Объект NotebookControler, отвечающий за выполнение бизнес-логики в блокноте, например «Сохранить блокнот», «Загрузить блокнот», «Новый блокнот». NotebookView отвечает за управление HTML, который используется для презентации. Он выполняет низкоуровневые вещи, такие как «получить / установить блокнот» «получить / установить имя блокнота». Он также прослушивает события DOM (onClick) и запускает бизнес-события (saveNotebook). Это моя попытка пассивного просмотра.

Я хочу, чтобы мой клиентский код javascript был объектно-ориентированным, разделенным на части и проверяемым модулем. Я хочу протестировать NotebookController с макетом NotebookView и наоборот. Это означает, что я не могу просто создать экземпляр NotebookView внутри NotebookController. Я тоже

  • Поместите некоторую логику в мой notebook.js, которая соединяет 2 вместе
  • В моем приложении есть глобальная функция, которая знает, как создать экземпляр каждого из них и связать их вместе
  • Используйте Dependency Injection, доморощенную или что-то вроде SquirrelIoc

В Java выбор естественен: используйте Spring. Но это не очень похоже на JavaScript. Что нужно сделать?

Ответы [ 6 ]

3 голосов
/ 07 марта 2009

Спасибо за понимание. В итоге я написал простую утилиту внедрения зависимостей JavaScript. После некоторого обсуждения и ваших комментариев мне пришло в голову, что DI был действительно правильным ответом, потому что:

  1. Это полностью отделяло проблемы проводки от бизнес-логики, в то время как логика проводки была близка к проводимым вещам.
  2. Это позволило мне в общем случае обеспечить обратный вызов «все вы подключены» для моих объектов, чтобы я мог выполнить трехфазную инициализацию: создать экземпляр всего, связать все, вызвать обратные вызовы всех и сообщить им, что они подключены .
  3. Было легко проверить отсутствие проблем с зависимостями.

Итак, вот утилита DI:

var Dependency = function(_name, _instance, _dependencyMap) {
    this.name = _name;
    this.instance = _instance;
    this.dependencyMap = _dependencyMap;
}

Dependency.prototype.toString = function() {
    return this.name;
}

CONCORD.dependencyinjection = {};

CONCORD.dependencyinjection.Context = function() {
    this.registry = {};
}

CONCORD.dependencyinjection.Context.prototype = {
    register : function(name, instance, dependencyMap) {
        this.registry[name] = new Dependency(name, instance, dependencyMap);
    }, 
    get : function(name) {
        var dependency = this.registry[name];
        return dependency != null ? dependency.instance : null;
    },

    init : function() {
        YAHOO.log("Initializing Dependency Injection","info","CONCORD.dependencyinjection.Context");
        var registryKey;
        var dependencyKey;
        var dependency;
        var afterDependenciesSet = [];
        for (registryKey in this.registry) {
            dependency = this.registry[registryKey];
            YAHOO.log("Initializing " + dependency.name,"debug","CONCORD.dependencyinjection.Context");

            for(dependencyKey in dependency.dependencyMap) {
                var name = dependency.dependencyMap[dependencyKey];
                var instance = this.get(name);
                if(instance == null) {
                    throw "Unsatisfied Dependency: "+dependency+"."+dependencyKey+" could not find instance for "+name;
                }
                dependency.instance[dependencyKey] = instance; 
            }

            if(typeof dependency.instance['afterDependenciesSet'] != 'undefined') {
                afterDependenciesSet.push(dependency);
            }
        }

        var i;
        for(i = 0; i < afterDependenciesSet.length; i++) {
            afterDependenciesSet[i].instance.afterDependenciesSet();
        }
    }

}
3 голосов
/ 06 марта 2009

Инъекция зависимости, вероятно, является вашей лучшей ставкой. По сравнению с Java некоторые аспекты этого легче реализовать в коде JS, поскольку вы можете передать объект, полный обратных вызовов, в ваш NotebookController. Другие аспекты сложнее, потому что у вас нет статического анализа кода, чтобы формализовать интерфейс между ними.

2 голосов
/ 06 марта 2009

Я бы сказал, просто соедините их вместе:

function wireTogether() {
  var v = new View();
  var c = new Controller();
  c.setView(v);
}

Но тогда, конечно, возникает другой вопрос - как вы тестируете функцию wireTogether ()?

К счастью, JavaScript - действительно динамический язык, поэтому вы можете просто назначить новые значения для View и Controller:

var ok = false;

View.prototype.isOurMock = true;
Controller.prototype.setView = function(v) {
  ok = v.isOurMock;
}

wireTogether();

alert( ok ? "Test passed" : "Test failed" );
1 голос
/ 30 декабря 2011

Существует также платформа для внедрения зависимостей для JavaScript: https://github.com/briancavalier/wire

1 голос
/ 06 января 2011

У меня есть инверсия управляющей библиотеки для javascript, я очень доволен этим. https://github.com/fschwiet/jsfioc. Он также поддерживает события, так что если вы хотите, чтобы событие запуска было хорошо. Он мог бы использовать больше документации ...

http://github.com/fschwiet/jsfioc

Другой (более новый?) Вариант, который имеет лучшую документацию и поддержку, - requireJS (http://requirejs.org/).

0 голосов
/ 06 марта 2009

Я попытаюсь сделать это, но это будет немного сложно, не увидев никакого реального кода. Лично я никогда не видел, чтобы кто-то делал такую ​​конкретную попытку (M) VC с JavaScript или IoC в этом отношении.

Прежде всего, с чем вы собираетесь тестировать? Если вы еще этого не сделали, посмотрите Тестовое видео YUI , в котором содержится полезная информация о модульном тестировании с использованием javascript.

Во-вторых, когда вы говорите «лучший способ связать это агрегирование», я, вероятно, просто сделал бы это как установщик с контроллером

// Production
var cont = new NotebookController();
cont.setView( new NotebookView() );

// Testing the View
var cont = new NotebookController();
cont.setView( new MockNotebookView() );

// Testing the Controller
var cont = new MockNotebookController();
cont.setView( new NotebookView() );

// Testing both
var cont = new MockNotebookController();
cont.setView( new MockNotebookView() );

Но это делает большое предположение о том, как вы спроектировали свой контроллер и уже просматривали объекты.

...