Richie

Sometimes at night when I look up at the stars, and see the whole sky just laid out there, don't you think I ain't remembering it all. I still got dreams like anybody else, and ever so often, I am thinking about how things might of been. And then, all of a sudden, I'm forty, fifty, sixty years old, you know?

Maverick.Net介绍

    Maverick.Net是Java社区开源MVC Web框架Maverick的.Net版本,相关资料可以查看项目主页。
    不管Maverick.Net的是非好坏,了解一下它的思想还是不错的。下面的内容是对Maverick.Net整体做一个简单的介绍,以求能够从全局的角度了解Maverick.Net一些相关概念,大致如何工作。
    另外,本人没有使用Maverick.Net开发过项目,只是看了它的架构、源码之后有一些想法,跟大家分享一下,也希望能为想了解Maverick.Net的人提供些参考。
 
    1. 主框架
    Maverick.Net处理HttpRequest典型的流程如下图:
图一:处理请求 Reference:MaverickLite Specification
 
    Maverick.Net将每一个HttpRequest映射成Command。很容易看到,Command的提交者为用户界面,也就是View;处理者应当是业务逻辑的控制器,也就是Controller。这样,Maverick.Net Framework在处理每一个Command时,主要是解决下面的问题:如何从用户界面(View)提交的参数生成Model?用哪个Controller处理特定的Command以及如何将Model传递给这个Controller?Controller处理结束之后如何将处理结果呈现给用户(对Maverick.Net而言就是根据Controller处理结果,选择返回哪个View给用户界面)以及如何将Model传递给返回的结果View?
    Maverick.Net通过一个配置文件进行配置,典型的Command配置节点如下:
   
    这是Maverick.Net项目例子Friendbook中一个提交用户注册的Command配置节点。Dispatcher接收到提交注册的HttpRequest之后,得到signupSubmit的Command Name,从而可以得到这个Command对象。Command将创建指定的Controller对象Friendbook.Ctl.SignupSubmit.Friendbook,并调用Controller进行处理。Controller处理结果应当是注册成功,需要转向注册成功之后的View;或者是发生错误,需要转向重新填写注册资料的View。Maverick.Net Framework规定Controller的返回结果必须是一个View的名字,这样,Command调用Controller处理之后,就知道应该呈现哪个View对象。上图中,如果注册成功(success),将调用name="success"的View;如果注册出错,例如两次输入的密码不一致等,将调用name="error"的View。name="success"的View类型为redirect,将使用当前的MaverickContext对象(聚合了HttpContext、Model等对象)重定向到另外一个名称为edit.m的Command对象(View的类型等后面再介绍);name="error"的View类型为document(默认类型,不需要写type="document"),将直接使用Server.Execute()方法得到View的HTML输出,并使用Transform对象,将用户注册结果View的HTML输出转换成整体页面的HTML代码,返回给客户端(Transform的概念,后面再介绍)。
 
    2. 主要的类、对象
    Maverick.Net主要的类或对象如下:Dispatcher、Command、Controller、View、Transform、MaverickContext。
    2.1 Dispatcher
    Dispatcher是一个HttpHandler类,用于拦截和处理HttpRequest,HttpRequest-Command的映射就是通过Dispatcher完成。
    典型的情况下,Maverick.Net使用.m后缀作为ISAPI扩展,映射到Command。这种情况下,所有浏览器端提交的HttpRequest,请求的都是以.m结束的虚拟文件名,被提交给Dispatcher。这个文件在服务器上并不存在,Dispatcher接收到类似login.m的请求之后,将.m后缀去掉,取login作为Command Name,获取Command对象,处理请求。
    Maverick.Net老的版本基本上就是照搬Java版本的Maverick,在View的层面上只支持象JSP中Servlet的写法,也就是纯粹的ASP方式,或者是以ASP方式编写的的aspx页面。ASP.Net环境下ViewState、Postback事件机制、用户控件等这些特性都不能使用,这对于ASP.Net环境是一种很大的损失。1.2版本开始,Maverick.Net的Controller比较完全的支持aspx的服务器端控件、用户控件等,这样就可以使用Maverick.Net框架,又基于ASP.Net进行设计和开发。
    在使用aspx类型的Controller时,Dispatcher的拦截作用被去掉了,而是使用ASP.Net默认的HttpHandler,页面类继承Maverick.Ctl.Aspx.ControllablePage,ControllablePage在页面的Render方法中切入Maverick.Net框架的控制。这种情况下,Command Name也不再是.m后缀方式,而直接变成对应的aspx页面名称,例如<command name="Default.aspx">
    另外,Dispatcher处理请求时,负责构造MaverickContext对象,使Command、Controller、View、Transform等在各自的处理期间,都能够得到相应的上下文信息。
    2.2 Command
    Command的作用类似一个指挥官,它根据Maverick.config文件的配置创建Controller对象,调用Controller进行处理;根据Controller的返回结果,选择相应的View对象并执行。
    Command可以没有Controller,这种情况下,对Command的请求,实际上就是将特定的View呈现给客户端。在Maverick.Net中,这是CommandSingleView。
    另外,也可以继承ICommand接口实现一些特殊功能或者说自定义的Command,例如Maverick.Net框架本身实现了两个特殊的Command:ReloadCommand和CurrentConfigCommand。CurrentConfigCommand用于查看当前配置文件的内容,这个Command读取Maverick.config配置文件的内容,直接输出配置文件中的xml。另外,Maverick.Net在第一次处理请求的时候,会读取Maverick.config中的内容,根据配置创建Command、View、Transform对象并缓存起来,后续的请求只是从缓存中取相应的对象。如果在第一次访问之后这些配置有更新,这些更新不会立即反映到缓存的对象中。ReloadCommand的作用就是根据当前的Maverick.config配置文件内容,重新创建缓存的对象。
    2.3 Controller
    Controller的职责是负责业务逻辑的处理。
    在典型的应用中,Controller首先通过MaverickContext中的HttpContext对象,获取从用户界面也就是View提交过来的参数,创建Model对象,使用Model完成对请求的业务逻辑处理。Controller不同的处理结果,将用来确定选择哪个View。在Controller的处理过程中可能会对Model进行更新,或者是有一些参数需要传递给View,这些将通过MaverickContext这个上下文信息对象完成。
    在aspx类型的Controller中,因为Dispatcher的拦截作用被丢弃,因此MaverickContext对象的创建,也是在Controller中完成的。
    2.4 View
    MVC的概念中,View负责将某个特定的Model,或者是整个Business Domain Model中某些角度的切片呈现给客户端。Maverick.Net中,将View分成多种类型。
    DocumentView:这是默认的View类型。这类型的View将配置一个文件,可能是asp或者aspx或者html等,调用Server.Execute()方法得到HTML的输出,发送给客户端。
    ForwardView:这类型的View使用当前的MaverickContext重定向到另外一个Command去执行。如果基于structs的方式做系统分析设计,一个业务操作可能会被分解成很多个Command/Action,将这些Command/Action组合起来就形成一个新的Command/Action,或者就对应到某一个业务操作。forward类型的View是用于对这种形式的支持。
    RedirectView:这类型的View,将直接使用Response.Redirect()方法重定向过去。因此也可以将forward类型的View看作是redirect类型的一个特例。实际上在Maverick.Net项目的示例中就有不少这样的用法。这是因为Dispatcher拦截了HttpRequest,类似Response.Redirect("edit.m")的语句执行后,仍然会被Dispatcher拦截,开始一个新的Command的执行。
    TrivialView:这类型的View,Maverick.Net将MaverickContext的Model对象直接进行输出(取出内容,直接输出)。这要求MaverickContext的Model对象是下面几种类型:String、System.Text.StringBuilder、System.IO.TextReader、Sytem.Xml.XmlNode。
    XmlSerializingView:这类型的View,将MaverickContext的Model序列化成xml格式后输出。这种View主要用于基于xml的网站方案,或者是提供系统接口之用途。
    2.5 Transform
    在Maverick.Net看来,View执行的结果,还不是呈现给客户端的最终输出,之间必须经由Transform进行相应的转换。
    Maverick.Net实现的Transform有:DocumentTransform、XsltTransform。从XsltTransform类型更容易理解Maverick.Net的Transform概念。对应于XsltTransform,View是一个xml或者是XmlSerializing类型的View,为了将这个xml转换成HTML输出,需要使用xsl/xslt文件进行转换,这就是XsltTransform的工作。
    对于DocumentTransform的理解,Transform这个单词意义上有点牵强。首先,DocumentTransform是针对于asp、aspx、html这种文档类型的View。在我们的映象里,这种类型的View,Maverick.Net使用的是Server.Execute()方法,这样已经直接得到HTML代码,何必再经过一个DocumentTransform处理?
    实际上,对于一个系统,工作页面会被分成不同的区域,例如页面的Banner区、菜单/功能列表区、通用工具栏等等。这些不同的区域,在后面是由不同的Controller负责处理,对应的也是不同的Model。在Command的处理中,特定的Command只会使用自己的Controller对象,负责对相应的Model进行处理,因此也只应当对页面中它所辖区域的显示负责。这样,View执行的结果就只是Command所辖区域的更新,而并不是整个页面。至于如何将这个区域的更新显示在整个页面中,这就是DocumentTransform的工作。
    目前Maverick.Net对于DocumentTransform用如下方式进行处理。假如是一个document类型的View,对应一个aspx文件。View在执行时使用Server.Execute()方法得到输出的HTML代码。但这个还不是最终发向客户端的完整输出,而只是其中的一部分,Maverick.Net将这个HTML片断保存在HttpContext.Items[""]中。DocumentTransform对象对应的也是一个aspx的页面,这个页面应当完成系统页面的整体布局,并从HttpContext.Items[""]中取出View执行后的HTML,输出在适当的位置。这样,使用Server.Execute()方法调用DocumentTransform的aspx页面后,就是完整的发向客户端的HTML了。
    其实Transform是考虑了页面的整体布局,一个View只会对某个区域负责的状况,但是它并不支持复杂的布局,只能说是轻量级吧。复杂的布局通过iFrame等实现,或者做些修改后结合Ajax方式进行局部更新。
    2.6 MaverickContext
    这是一个上下文对象,主要负责在Command、Controller、View之间传递信息。它包含HttpContext、Model和一个IDictionary类型的Params属性。
    HttpContext对象在创建MaverickContext时由创建者(Dispatcher或者ControllablePage)提供;Model由Controller执行处理时创建;Params用于在需要的传递其它的参数。
 
    从MVC的角度来看处理流程,如下图:
   
 
    3. 简评
    在其它一些介绍Maverick.Net的文章里,有看到不少对Maverick.Net,对ASP.Net下如何运用MVC概念的讨论,看法各不一样。其实对.Net下的开发、架构设计的运用,本来都是仁者见仁,智者见智的事情,只是看大家怎么运用了。下面的这些感想,只是针对企业级应用的架构模式方面,对基于快速开发的小型项目当然另当别论。
    3.1 M-V-C分离
    ASP.Net本身的确就是一种MVC的框架,可以说是一个很强大又很好用的MVC框架,但是有一些障碍在阻挠着我们。ASP.Net本身的许多特性,使得M-V-C之间的概念变得模糊甚至混乱。
    看一下这个场景:添加一个Web Form;用Drag-Drop方式在Form中添加GridView、Connection等数据库组建;设置相应的属性,例如数据库连接,GridView的数据源绑定等。可能就简简单单1、2行代码甚至1行都不需要,运行这个Web Form就将数据显示出来了。一切是这么方便和简单,给BS开发带来一种革命性的思想。可是基于企业级应用架构层面来看,从这样的场景中你能得到什么,想到什么?
    当然,对富于经验的人,这根本就不是问题所在。随便写一段代码,就能很好的体现MVC的思想,无所谓使用的哪种语言,什么工具。假如现在需要几百个开发者协同完成一个项目,情况会怎样?ASP.Net本身在架构方面没有太多约束力,尽管它是一个很好的MVC框架。而这种情况下,又非常的需要在架构上对协作的开发者进行约束、规范,否则怎样确保按照架构、设计方案进行开发,就是件灾难性的事情。
    Maverick.Net在MVC的呈现上是非常清晰的。也许你会发现,在你的架构中正是需要引入这样的一种思想。不管是哪个子团队,哪一个人,使用这一个框架,就需要理解它的思想,按照它的规范进行开发。
    3.2 分析设计思想的转变 MDA
    从大的视角来看,用户需求与软件系统之间的边界,就是UI表现层的东西,也就是View。一套系统最终都是通过View向用户,或者是通过接口向其它系统,呈现出它的全部内容。系统开发是对需求的实现,而架构设计则是对需求的抽象。
    在小型项目中,可以不需要什么方法论。把握了需求之后,就着重在表现层的设计,与用户确认好之后,以这个为目标把系统开发出来就OK了。这是很直接的一种方式,也是人的思维模式中比较容易接受的一种方式。在企业级应用的架构中,如果在不同程度上以这种方式驱动,或多或少的以表现层为主体,就会妨碍我们的抽象行为,也就是影响到架构的质量。
    MDA的思想中,强调的是先从需求中提取Business Model,然后以Model为主体,对业务逻辑进行分析。在框架层面将M-V-C比较清晰的区分开来,有利于在分析设计思想方面向MDA方式的转换。
    3.3 简单,而又强大的框架
    忘了是在哪里看到的这样一句话。说它简单,因为整个Maverick.Net项目的代码很少,用于实现的主要对象也不多,它仅仅用一个框架性的协议把M-V-C之间作分离,对Model、View、Controller方面并不做任何过多的扩展、处理。所有关于View的呈现方面、Controller的架构方面等,完全交由使用者自行决定。说它强大,因为整个框架的可扩展性相当不错。
    但并不是说这么好的一个框架,拿过来用就是了。说它好,可能很大程度上是在指它的思想层面,并不能代表它目前的实现就是最好的或者说非常合理的。随便列举几点:
    a) 有考虑多语言的方案,但是没有实现。
    b) 它的Controller是基于类的层面配置,这个该如何使用?
    方案1:把它的Controller当作Business Facade,所以可能需要为每个Command写一个Controller类,用这个类去整合与Business Model相对应的各个Business Rule类。这种方案有利于分布式、SOA的运用,但是每个Command开发个Controller显得有点繁琐。
    方案2:把Controller作为完整的Business Logic封装,在上下文中使用参数等其它机制,将Command关联到Controller的方法层面上。
    c) Controller + View类型的选择
    方案1:使用aspx类型的Controller。这种方式的优点在于能够使用ASP.Net的机制,包括常规的PostBack事件机制、用户控件等,或者整合ASP.Net其它一些东西,例如Atlas等,可能都会比较方便。但以我个人的观点来看,不提倡将*.aspx.cs作为Controller,而是纯粹的作为View的一部分。*.aspx.cs只负责利用ASP.Net的机制,从页面获取参数、创建Model对象传递给控制类;在从控制类重新得到Model等相关信息之后(Controller处理完毕的结果),再利用ASP.Net机制将这些信息进行呈现。
    当然,将*.aspx.cs作为轻量级的Business Facade也是可以考虑的方案。
    方案2:使用xml或者Maverick.Net标准的document方式。
    其实这两种方式之间的差别还是很大的。使用xml方式的话,将使用XsltTransform进行转换,可能需要编写大量的xslt文件。使用Maverick.Net标准的document方式,是指完全类似ASP的方式了,使用DocumentTransform进行转换。这两种方式都是使用Maverick.Net的典型工作方式,即以.m作为请求页面的后缀,由Dispatcher对HttpRequest进行处理。在页面编写时,它们都不能使用ASP.Net特性了,说这种方式下完全抛弃了ASP.Net机制也不过分。所以对于ASP.Net开发者将它们视为同一种方式也不奇怪了。
    直接使用这种方式开发,可能没有多少人能够接受。但是基于这种方式,在View的开发方面下一些功夫,例如封装、通用化模板的扩展、添加适用于Maverick.Net的Ajax方案等,是能够在一定程度上降低这种方式下View开发的繁琐性,达到基本可以接受的程度(相对于ASP.Net开发者而言)。

    2006-09-11:补充一副图

posted on 2006-09-10 15:57  riccc  阅读(3901)  评论(4编辑  收藏  举报

导航