Model view controller 是Ext JS';MVC是反模式的吗?

Model view controller 是Ext JS';MVC是反模式的吗?,model-view-controller,extjs,anti-patterns,Model View Controller,Extjs,Anti Patterns,我在一个由25名开发人员组成的团队中工作。我们使用Sencha的ExtJS MVC模式。但是我们相信他们对MVC的定义是误导性的。也许我们也可以称他们的MVC为反模式 在MVC控制器中,AMAIK只知道视图的名称或路径,而不知道视图的内部结构。例如,无论view是将客户列表呈现为简单的下拉列表还是自动完成列表,都不是控制器的责任 然而,在ExtJS的MVC中,控制器应该知道视图元素的呈现,因为控制器钩住这些元素,并监听它们的事件。这意味着,如果视图的某个元素发生更改(例如按钮变为链接),则控制器

我在一个由25名开发人员组成的团队中工作。我们使用Sencha的ExtJS MVC模式。但是我们相信他们对MVC的定义是误导性的。也许我们也可以称他们的MVC为反模式

在MVC控制器中,AMAIK只知道视图的名称或路径,而不知道视图的内部结构。例如,无论view是将客户列表呈现为简单的下拉列表还是自动完成列表,都不是控制器的责任

然而,在ExtJS的MVC中,控制器应该知道视图元素的呈现,因为控制器钩住这些元素,并监听它们的事件。这意味着,如果视图的某个元素发生更改(例如按钮变为链接),则控制器中的相关选择器也应更改。换句话说,控制器与视图的内部结构紧密耦合


谴责ExtJS的MVC是反模式的理由可以接受吗?控制器与视图耦合对吗?

我每天都使用ExtJS4的MVC。我有一个优雅的MVC应用程序,它严格定义了概念的分离,并且维护和扩展起来非常简单,而不是意大利面条式的代码。也许您的实现需要进行一些调整,以充分利用MVC方法提供的功能。

更新(2015年3月):引入的Ext 5.0应该解决本线程中讨论的大多数问题。优点:

  • 在ViewController内的组件引用周围更好/更强制的范围
  • 更容易将视图特定逻辑与应用程序流控制逻辑分开封装
  • 由框架及其关联视图管理的ViewController生命周期
Ext5仍然提供现有的
Ext.app.Controller
类,以保持向后兼容,并为如何构造应用程序提供更多灵活性

原始答案:

在ExtJS的MVC中,控制器应该知道视图元素的呈现,因为控制器钩住这些元素并侦听它们的事件。这意味着,如果视图的某个元素发生更改(例如按钮变为链接),则控制器中的相关选择器也应更改。换句话说,控制器与视图的内部结构紧密耦合

事实上,我同意在大多数情况下,由于您所引用的确切原因,这不是最佳选择,而且不幸的是,Ext和Touch附带的大多数示例演示了经常使用违反视图封装的选择器定义的引用和控制功能。然而,这不是MVC的要求——这只是示例的实现方式,而且很容易避免

顺便说一句,我认为在真正的应用程序控制器(控制应用程序流和共享业务逻辑,应该与视图完全不耦合——这些就是您所指的)和视图控制器之间区分控制器样式绝对有意义(特定于视图的控制/事件逻辑,通过设计紧密耦合)后者的一个例子是在一个视图中的小部件之间进行协调的逻辑,完全在该视图的内部。这是一个常见的用例,将视图控制器耦合到它的视图并不是一个问题——它只是一种代码管理策略,使视图类尽可能保持沉默

问题是,在大多数有文档记录的示例中,每个控制器只引用它想要的任何东西,这不是一个好的模式。然而,这不是Ext的MVC实现的要求——这只是示例中使用的(惰性?)约定。这非常简单(我认为是可取的)而是让视图类为应向应用程序控制器公开的任何内容定义它们自己的自定义getter和事件。
refs
config只是一个简写——您可以随时调用类似于
myView.getSomeReference()的内容
自己,并允许视图指定返回的内容。而不是
this.control('some>view>widget')
只需在视图上定义一个自定义事件,并在该widget执行控制器需要知道的操作时执行
this.control('myevent')
。很简单

缺点是这种方法需要更多的代码,对于简单的情况(如示例)可能会有些过分。但我同意,对于实际应用程序或任何共享开发,这是一种更好的方法


是的,将应用程序级控制器绑定到内部视图控件本身就是一种反模式。但是Ext的MVC不需要它,而且避免自己这么做非常简单。

当然,控制器以某种方式绑定到视图。您需要准确地定位视图中您想要侦听的元素

听那个按钮的点击声,听那个表单元素的变化,听那个自定义组件/事件

MVC的目标是组件解耦和可重用性,Sencha MVC在这方面非常棒。正如@bmoeskau所说,在分离视图控制器(视图/小部件本身的内置)和应用程序控制器(顶级视图操作)时必须小心充分利用MVC模式。这在阅读时并不明显。重构MVC方法,创建不同的控制器,创建自定义组件,并采用完整的ExtJS MVC架构来利用它


Sencha方法IMHO中仍然存在一个小问题,当应用程序中有多个相同视图的实例时,MVC
refs
系统实际上不起作用。例如:如果有一个TabPanel,其中包含同一组件的多个实例,那么refs系统将被破坏,因为它将始终以DOM中找到的第一个元素为目标这些都是解决方法,但我希望这将很快得到解决。

我认为如果您使用Sencha架构师来生成
//Designer Generated
Ext.define('MyApp.view.MainView', {
    extend: 'Ext.grid.GridPanel',
    alias: 'widget.mainview',    
    initComponent: function() {
    }
});


//Your View Decorator
Ext.define('MyApp.view.MainView', {
    extend: 'MyApp.view.MainViewEx',
    alias: 'widget.mainviewex',    
    initComponent: function() {
    this.mon(this, 'rowselect', function(){
        this.fireEvent('userselected', arguments);
    }, this);
    }
});
Ext.ns('Test');

Test.MainPanel = Ext.extend(Ext.Container, {
    initComponent : function() {
        this.panel1 = new Test.Panel1({
            listeners: {
                firstButtonPressed: function(){
                    this.panel2.addSomething();
                },
                scope: this
            }
        });
        this.panel2 = new Test.Panel2();

        this.items = [this.panel1,this.panel2];

        Test.MainPanel.superclass.initComponent.call(this);
    }

});

Test.Panel1 = Ext.extend(Ext.Panel, {
    initComponent : function() {
        this.addEvents('firstButtonPressed');

        this.tbar = new Ext.Toolbar({
           items: [{
             text: 'First Button',
               handler: function(){
                   this.fireEvent('firstButtonPressed');
               }
           }] 
        });

        Text.Panel1.superclass.initComponent.call(this);
    }
});

Test.Panel2 = Ext.extend(Ext.Panel, {
    initComponent : function() {
        this.items = [new Ext.form.Label('test Label')]

        Test.Panel2.superclass.initComponent.call(this);
    },

    addSomething: function(){
        alert('add something reached')
    }
});
Ext.define('MyApp.controller.Earmarks', {
    extend:'Ext.app.Controller',
    views:['earmark.Chart'],


    init:function () {
        this.control({
             'earmarkchart > toolbar > button':{
                click:this.onChartSelect
            },
            'earmarkchart tool[type=gear]':{
                click:this.selectChart
             }
        });
    }
});