Каков правильный способ соединения двух объектов javascript?
В настоящее время я сталкиваюсь с загадкой: как правильно подключить 2 объекта javascript?
Представьте себе приложение, такое как текстовый редактор с несколькими различными файлами. У меня есть HTML-страница, представляющая представление для ноутбука. У меня есть файл notebook.js, который содержит определения классов для NotebookController и Notebook View.
Объект NotebookControler, отвечающий за выполнение бизнес-логики на ноутбуке, например "Сохранить записную книжку", "Загрузить блокнот", "Новый ноутбук". NotebookView отвечает за управление HTML, который используется для презентации. Он делает вещи низкого уровня, такие как "получить/установить блокнот", "получить/установить название ноутбука". Он также прослушивает события DOM (onClick) и запускает бизнес-события (saveNotebook). Это моя попытка в пассивном представлении.
Я хочу, чтобы мой клиентский код javascript был объектно-ориентированным, разделенным интересам и проверяемым единицей. Я хочу протестировать NotebookController с макетным NotebookView и наоборот. Это означает, что я не могу просто создать экземпляр NotebookView внутри NotebookController. Так что я
- Поместите некоторую логику в свой блокнот .js, который соединяет 2 вместе
- У меня есть глобальная функция в моем приложении, которая знает, чтобы создать экземпляр одного из них и связать их вместе.
- Используйте Injection Dependency, либо самодельный, либо что-то вроде SquirrelIoc
В Java выбор является естественным: используйте Spring. Но это не похоже на JavaScript-y. Какая правильная вещь?
Ответы
Ответ 1
Инъекционная инъекция, вероятно, лучше всего. По сравнению с Java, некоторые аспекты этого проще сделать в JS-коде, поскольку вы можете передать объект, полный обратных вызовов, в ваш NotebookController. Другие аспекты сложнее, потому что у вас нет статического анализа кода для формализации интерфейса между ними.
Ответ 2
Спасибо за понимание. Я закончил тем, что написал простую утилиту для загрузки зависимостей JavaScript. После долгого обсуждения и ваших комментариев, мне показалось, что DI действительно был правильным ответом, потому что:
- Это полностью отделяет проблемы проводки от бизнес-логики, сохраняя логику проводки близко к проводным вещам.
- Это позволило мне в общем случае обеспечить обратный вызов "вы все подключены" к моим объектам, чтобы я мог выполнить трехфазную инициализацию: создать экземпляр всего, подключить его все, вызвать все обратные вызовы и сообщить им, что они подключены.
- Было легко проверить отсутствие проблем с ошибками.
Итак, здесь утилита 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
Я бы сказал, просто соедините их вместе:
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" );
Ответ 4
У меня есть инверсия библиотеки управления для javascript, я очень доволен ею. https://github.com/fschwiet/jsfioc. Он также поддерживает события, поэтому, если вы хотите, чтобы событие запуска было прекрасным. Он может использовать дополнительную документацию...
http://github.com/fschwiet/jsfioc
Другой (более новый?) вариант, который имеет лучшую документацию и поддержку, является requireJS (http://requirejs.org/).
Ответ 5
Существует также структура для инъекции зависимостей для JavaScript: https://github.com/briancavalier/wire
Ответ 6
Я попытаюсь взять удар, но это будет немного сложно, если не увидеть какой-либо фактический код. Лично я никогда не видел, чтобы кто-то делал такую конкретную попытку (M) VC с JavaScript или IoC, если на то пошло.
Прежде всего, с чем вы собираетесь протестировать? Если вы еще этого не сделали, проверьте YUI Test video, в котором есть хорошая информация об модульном тестировании с помощью 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() );
Но это делает некоторое большое предположение о том, как вы уже разработали свой контроллер и просмотрите объекты.