2

MVC起源

几乎所有应用系统都是由这三部分组成:表示企业数据和业务规则的数据,用来展示数据的,用户看到并与之交互的界面,负责转发请求,对请求的处理;协调数据和展示的逻辑。在MVC出现之前,这三部分都是写在一起的,是耦合的,这对软件工程是不好的。

1979年,Trygve Reenskaug在Smalltalk-80系统上首次提出了MVC的概念,最初的时候叫做Model-View-Controller-Editor。1994年,Gof(Gang of Four)在 Design Patterns: Elements of Reusable Object-Oriented Software 一书中对MVC模式做了深入的解析。

Trygve Reenskaug最初提出MVC的目的是为了把数据(Model)和视图(View)分离开来,然后用控制器(Controller)作胶水来粘合M和V之间的关系。很显然,这样做的目的是为了实现注意点分离这样一个更高层次的设计理念,View就只负责视图相关的东西,Model就只负责描述数据模型,Controller负责总控,各自协作。

传统MVC

MVC模式

通过把职责、性质相近的成分归结在一起,不相近的进行隔离,MVC将系统分解为模型、视图、控制器三部分,每一部分都相对独立,职责单一,在实现过程中可以专注于自身的核心逻辑。MVC是对系统复杂性的一种合理的梳理与切分。

MVC

Model对view和controller都可知,view对controller可知,controller对谁都不可知。

这里为了容易理解,我想用我们实验室三个屋来打个比方,小屋是view,中屋是controller,大屋是model。

  1. 用户请求、试图选择:小屋不认识中屋,但是中屋认识小屋,小屋一旦有需求,中屋就会默默地满足他的要求,比如:小屋的贾老师大喊一声“把三木球拉出去”,中屋就来了一个人把三木球拉了出去。

  2. 状态查询:小屋认识大屋,小屋想知道大屋的郭师兄今天穿了件什么颜色的衣服,就跑去看了看,哦,原来郭师兄穿了件绿色的衣服。

  3. 用户请求、状态改变、通知改变:贾老师大喊一声“给郭师兄换件红色的衣服”,隔壁屋的一个人就去给他换了,郭师兄换完衣服,开心地大喊了一句“我换了件红色的衣服!”。

其实这里,我对状态的理解还是有些模糊,暂且把它当做数据,比如登录,某种角色的用户登录后,controller会告诉model改变状态,然后通知给view,也就是展示给该角色的用户相应的数据。

MVC实现

GoF (Gang of Four,四人组, 《Design Patterns: Elements of Reusable Object-Oriented Software》/《设计模式》一书的作者:Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides)并没有把MVC提及为一种设计模式,而是把它当做"一组用于构建用户界面的类集合"。在他们看来,它其实是其它两个个经典的设计模式的演变:观察者模式(Observer)(Pub/Sub)和策略模式(Strategy)。

正如我们所讨论的,models表示应用的数据,而views处理屏幕上展现给用户的内容。为此,MVC在核心通讯上基于推送/订阅模型。

model与view的推送/订阅

view:
models[0...n];
callbackModelEvents[0...n];

for(model 0...n)
    for(model[i].Event(0...n))
        this.on(Events[j], callbackModelEvent[k]);

View(订阅者)注册了一组model和model对应的事件的处理函数,当model(发布者)一旦发布了消息,view就会接收。

view与controller的推送/订阅

View和controller同上。

Extjs下的MVC

Extjs框架

Extjs这个框架是典型的MVC模式,它帮我们完成了MVC的大部分工作,为了体现MVC模式,提供给我们这样的接口,当然,我们用户的主要代码就是写在这里的。

Extjs工程目录

这是ExtJs4.2的工程文件目录,我们的代码就写在controller、model、store、view这四个文件夹下,接下来,我们看一下Ext代码是怎么写的。

Ext.define("Srims.view.patent.PatentList", {
    extend: 'Ext.grid.Panel',
    store: [patent.patents],
    ……
});

在view中,我们可以配置当前view对应的store(即数据),当数据更新时,从model发出消息,展示在view中,当然,我们需要配置这么一行代码,剩下的框架已经帮我们做好了。

Ext. define('Srims.controller.patent.patentList', {
    extend: 'Ext.app.Controller',
    views: ['patent.patentList'],
    model:[patent.patents],
    ……
    'patentlist button[itemId=btnDeletePatent]': { //删除按钮点击响应
            click: function (button, e) {
                Ext.MessageBox.confirm('确认', '确认删除选中记录么?',
                    function (btn) {
                        if (btn == 'yes') {
                            var panel = button.up('panel');
                              var selectionRecord = panel.getSelectionModel().getSelection()[0];

                              var store = panel.getStore();
                            store.remove(selectionRecord);

                        }
                    }, this);
                    ……
            }
        },
            ……
});

在controller中,可以配置当前controller对应的view、model,在方法中也可以对view对应的store操作,也可以直接创建store(model)的实例,对其操作。

通过这样配置,可以实现观察者模式。

问题一:view复用的问题

view与controller的多对多关系

在Ext中,view和controller是多对多的关系,比如,controller对它注册的多个view进行消息响应或者操作,假如要选择view1一般通过get方法,就是get加view1作为方法名,使用的,这样看上去是没问题的。

view与controller的类和实例的相互关系

但是Ext是基于类的,实际应用中,我们通常是这样的,由于很多view长得很像甚至相同,我们习惯复用同一个view类,这样,一个view类对应着多个实例。我们在controller中的所有方法都是针对他对应的view类的,当我们创建了一个controller实例,通过该类中的方法找到对应的view类,来操作这个view类对应的view实例,这时候,一个view对应着多个实例,操作就会产生混乱。

当然,只要我们在编码是注意通过controller中适当处理逻辑,在view中适当添加标志位,混乱和冲突也是可以解决的。所以今后,适当复用并且提高编码能力,也是没问题的。

问题二:粒度问题

在传统的MVC架构里包括Extjs,一个controller可能有多个方法,每个方法往往对应一个user action,因此,一个controller往往对应多个user action。传统MVC架构里将一个user action委派到某个controller的某个方法的过程。

目前,很多平台的主流MVC框架在设计上都引入了command模式,command模式的引入改变了传统MVC框架的结构,受冲击最大的就是 controller。

在基于command的MVC架构里,一个command往往只对应一个user action,在该架构里变成了将useraction与command一一绑定的过程。

controller向command的转变

主流MVC框架向command转型一个非常重要的原因就是:由于缺少合理的组织依据,controller的粒度很难拿捏。

controller不同于view与model,view与model都有各自天然的粒度组织依据,view的组织粒度直接承袭用户界面设计,model的组织粒度则是依据某种分析设计思想(如OOA/D)进行领域建模的结果,controller需要同时协调view与model,但是view与model的组织结构和粒度都是不对等的,这就使得controller面临一个"在多大视图范围内沟通与协调多少领域对象"的问题,由于找不出合理的组织依据,设计者在设计controller时往往感到无所适从。

Extjs虽然在这方面考虑欠佳,但我们能做的就是遵循良好的设计原则,对某些较"大"的user action方法进行分解,从中抽离出一些可复用的部分封装成一些较"小"的方法,尽量减小代码的冗余。


竹与蜻蜓
123 声望9 粉丝

没有故事的女同学


引用和评论

0 条评论