简体   繁体   English

将2个javascript对象连接在一起的正确方法是什么?

[英]What is the right way to wire together 2 javascript objects?

I'm currently facing a conundrum: What is the right way to wire together 2 javascript objects? 我目前面临一个难题:将两个javascript对象连接在一起的正确方法是什么?

Imagine an application like a text editor with several different files. 想象一下像文本编辑器这样的应用程序,它有几个不同的文件。 I have some HTML page that represents the view for the notebook. 我有一些HTML页面代表笔记本的视图。 I have a file notebook.js that contains class definitions for NotebookController and Notebook View. 我有一个文件notebook.js,其中包含NotebookController和Notebook View的类定义。

NotebookControler object responsible for performing business logic on the Notebook like "Save Notebook," "Load Notebook," "New Notebook." NotebookControler对象负责在笔记本上执行业务逻辑,如“保存笔记本”,“加载笔记本”,“新笔记本”。 NotebookView is responsible for managing the HTML that is used for presentation. NotebookView负责管理用于演示的HTML。 It does low level stuff like "get/set notebook body" "get/set notebook name." 它做低级别的东西,如“获取/设置笔记本电脑主体”“获取/设置笔记本名称”。 It also listens for DOM events (onClick) and fires business events (saveNotebook). 它还侦听DOM事件(onClick)并触发业务事件(saveNotebook)。 This is my attempt at the Passive View pattern. 这是我对被动视图模式的尝试。

I want my javascript client-side code to be object-oriented, separated concerns, and unit-testable. 我希望我的javascript客户端代码是面向对象的,分离的关注点和单元可测试的。 I want to test NotebookController with a mock NotebookView and vice versa. 我想用模拟NotebookView测试NotebookController,反之亦然。 This means that I can't just instantiate a NotebookView inside the NotebookController. 这意味着我不能只在NotebookController中实例化NotebookView。 So do I 我也是

  • Put some logic in my notebook.js that wires the 2 together 在我的notebook.js中放入一些逻辑,将2连接在一起
  • Have a global function in my application that knows to instantiate one of each and wire them together 在我的应用程序中有一个全局函数,它知道实例化其中一个并将它们连接在一起
  • Use Dependency Injection, either a home-grown one or something like SquirrelIoc 使用依赖注入,可以是本土的,也可以是SquirrelIoc

In Java, the choice is a natural one: use Spring. 在Java中,选择是自然的:使用Spring。 But that doesn't seem very JavaScript-y. 但这似乎不是JavaScript-y。 What's the right thing to do? 什么是正确的做法?

Dependency injection is probably your best bet. 依赖注入可能是你最好的选择。 Compared to Java, some aspects of this are easier to do in JS code, since you can pass an object full of callbacks into your NotebookController. 与Java相比,JS代码中的某些方面更容易实现,因为您可以将一个充满回调的对象传递到NotebookController中。 Other aspects are harder, because you don't have the static code analysis to formalize the interface between them. 其他方面更难,因为您没有静态代码分析来形式化它们之间的接口。

Thanks for the insight. 感谢您的见解。 I ended up writing a simple JavaScript dependency injection utility. 我最终编写了一个简单的JavaScript依赖注入实用程序。 After debating for a while and your comments, it occured to me that DI was really the right answer because: 经过一段时间的辩论和你的评论之后,我发现DI确实是正确答案,因为:

  1. It totally separated the concerns of wiring from the business logic while keeping the wiring logic close to the things being wired. 它完全将布线问题与业务逻辑分开,同时保持布线逻辑接近有线连接。
  2. It allowed me to generically provide a "you're all wired up" callback on my objects so that I could do a 3 phase initialization: instantiate everything, wire it all up, call everyone's callbacks and tell them they're wired. 它允许我通常在我的对象上提供一个“你全部接线”回调,这样我就可以进行三阶段初始化:实例化所有内容,连接所有内容,调用每个人的回调并告诉他们他们已经连线。
  3. It was easy to check for dependency missing problems. 很容易检查依赖性缺失问题。

So here's the DI utility: 所以这是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();
        }
    }

}

I would say, just wire them together: 我会说,只需将它们连接在一起:

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

But then of course another question raises - how do you test the wireTogether() function? 但当然另一个问题提出了 - 你如何测试wireTogether()函数?

Luckily, JavaScript is a really dynamic language, so you can just assign new values to View and Controller: 幸运的是,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" );

I have a inversion of control library for javascript, I'm pretty happy with it. 我有一个javascript控件库的反转,我很高兴。 https://github.com/fschwiet/jsfioc . https://github.com/fschwiet/jsfioc It also supports events, so if you want to have a startup event thats fine. 它还支持事件,所以如果你想要一个很好的启动事件。 It could use more documentation... 它可以使用更多文档......

http://github.com/fschwiet/jsfioc http://github.com/fschwiet/jsfioc

Another (newer?) option, which has better documentation and support, is requireJS (http://requirejs.org/). 另一个(较新的?)选项,具有更好的文档和支持,是requireJS(http://requirejs.org/)。

还有一个用于JavaScript依赖注入的框架: https//github.com/briancavalier/wire

I'll try to take a stab at this, but it will be a little difficult without seeing any actual code. 我会尝试对此进行尝试,但如果没有看到任何实际代码,那将会有点困难。 Personally, I've never seen anybody do such a specific attempt at (M)VC with JavaScript or IoC for that matter. 就个人而言,我从未见过有人在(M)VC上使用JavaScript或IoC进行过这样的特定尝试。

First of all, what are you going to test with? 首先,你要测试什么? If you haven't already, check out the YUI Test video which has some good info on unit testing with javascript. 如果您还没有,请查看YUI测试视频 ,其中包含有关使用javascript进行单元测试的一些好信息。

Secondly, when you say "the best way to wire up that aggregation" I would probably just do it as a setter w/the controller 其次,当你说“连接聚合的最佳方式”时,我可能会把它当作一个带控制器的setter

// 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() );

But this makes some big assumption on how you've designed your controller and view objects already. 但是这对你如何设计控制器和查看对象做了一些很大的假设。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM