Spiga

以服务器端为中心的 ASP.NET AJAX 模式 (Part 1 - Behavior)

2008-10-26 20:33 by Cat Chen, 3965 visits, 网摘, 收藏, 编辑

早在ASP.NET AJAX从CTP转向Beta再转向RTM时,看着客户端的Control被逐步放弃,与此同时ASP.NET AJAX Control Toolkit越来越多地使用Behavior,我就想深入说说ASP.NET AJAX的模式。不过由于我比较懒,所以这个话题只在《理想的 ASP.NET AJAX (Part 2 - Server Centric)》中一笔带过,没有深入讨论。今天看到volnet的《我们究竟是否有在“Asp.net中模仿Winform的MessageBox ”的必要?》,决定写一个文章系列来说说ASP.NET AJAX的模式。

什么是Behavior?

什么是Behavior?Behavior与Control有什么不同?这是首先需要回答的问题。

在Windows开发当中,Behavior的概念是不存在的,有的只是Control。ASP开发连Control都没有的,到了ASP.NET才引入了Control的概念。为什么Ajax开发要引入Behavior这样的概念呢?因为Behavior意味着不需要改变原有的组件逻辑,而改变原有组件的逻辑在客户端往往是不可行的,至少是难以实现的。

举个最简单的例子,一个<input type=”text” />就是一个浏览器内部的对象,你无法扩展这个对象的类型,也无法为它加上新的属性与方法,至少并非所有浏览器都允许你在JavaScript中这样做。然而如果你想要让它加上auto-complete(或曰suggest)的功能,这是可以做到的,并且很多人都做过了,例如ASP.NET AJAX Control Toolkit的AutoComplete,或者是script.aculo.us的autocompleter。这些实现都基于同一种方式,就是尝试基于input已有的接口在它之外添加新功能,而非尝试继承input并在它之内添加功能。

类似的做法在Ajax开发中普遍存在。例如说拖放吧,现在离HTML5拖放的全面普及不知道还有多远,所以大家都只能基于现有的鼠标事件来开发拖放功能。又或者说带有验证功能的输入框,无论是input还是select,无论是客户端验证还是服务器端验证,也都是基于现有HTML元素的事件来完成的。这一切都是Behavior。

什么情况下使用Behavior?

简单归纳,就两个条件:

  1. 需要基于特定的一个组建进行扩展
  2. 组建本身所处的环境缺乏可扩展性

单看第1个条件,我们有丰富的选择。很多人的第一反应就是继承自该组件,把扩展功能做到子类里面。熟悉设计模式的话,可能还会想到decorator pattern。然而在浏览器的环境当中,受到第2个条件的限制,继承或者decorator pattern都是不可行的。这时候,我们就需要使用Behavior了。

在一定程度上,我们可以把Behavior看作一种折衷了的decorator pattern。在decorator pattern中,decorator也继承自组件,因此当一个组件使用decorator pattern后,我们就把decorator放在原组件所处的位置上,而原组件就成了decorator的一个子节点(基于树的角度来看的话)。如果再加一个decorator,原decorator就会如同一个普通组件那样再被封装一次。在多个decorator的情况下,decorator之间是串联的关系。Behavior本身不是一个浏览器内部的组件,它无法继承自input的基类,因此Behavior也不能串联。Behavior本身本也不会取代input在树中的位置,这使得Behavior可以并联起来——一个input可以有多个Behavior,例如一个是自动完成,另外一个是输入验证。

总之,如果你原来的工作就是和UI打交道,并且熟练使用decorator pattern,那么在进行Ajax开发时把decorator pattern换成Behavior就可以了。

小结

回过头来看文章开头所说的MessageBox(或曰confirm)的问题,如果这个功能不需要对服务器端进行反馈,完全可以使用Behavior实现。ASP.NET AJAX Control Toolkit就有ConfirmButton这样一个东西,在客户端叫Behavior,在服务器端叫Extender,其实Extender就是对Behavior在服务器端做一下封装而已。当然,ConfirmButton不能完全实现MessageBox的功能,它只能在用户选择“取消”时取消整个提交操作,但不能够执行另外一个服务器端的代码分支。这算是Behavior的一个限制,就是它只能在客户端添加额外的功能,但是它不能影响到与服务器端的交互。但很多时候,正是这种限制确保了我们开发的组件是与服务器端解耦的。

如果我真的需要一个与服务器端交互的客户端组件,例如支持调用服务器端代码分支的confirm,怎么办?这时候你就真的需要一个与服务器端Control对应的客户端Control了。请关注本系列的下一篇文章,《以服务器端为中心的 ASP.NET AJAX 模式 (Part 2 – Control)》。通过订阅feed,你可以及时获得文章更新:

P.S.既然提到了Behavior和Extender的一一对应关系,就不得不说一下,服务器端的Extender就是真正的decorator pattern。

0
0
(请您对文章做出评价)
« 上一篇:Vista 为什么要引入 UAC
» 下一篇:以服务器端为中心的 ASP.NET AJAX 模式 (Part 2 - Control)
Add your comment

12 条回复

  1. #1楼 要有好的心情      2008-10-26 23:18
    "但不能够执行另外一个服务器端的代码分支。这算是Behavior的一个限制,就是它只能在客户端添加额外的功能,但是它不能影响到与服务器端的交互。"Behavior可以与服务器打交道吧,Behavior截获或添加Dom元素的客户端事件,在这些事件里可以用Sys.Net.WebRequest吧
      回复  引用  查看    
  2. #2楼[楼主] Cat Chen      2008-10-27 00:06
    @要有好的心情
    是我说得不够严谨,其实我想强调的是不能够和一般的Page流程进行交互。调用WebService(包括WebMethod)当然是可以的,但是要让这和Page流程交互不仅仅麻烦,还引入了各种各样的紧耦合,显然是一种不优雅的用法。

    我觉得该走Page流程的就走Page流程,该走WebService的就走WebService,两者应该严格区分。
      回复  引用  查看    
  3. #3楼 坐断东南 笑煞之!!      2008-10-27 09:58
    客户端的Behavior,在不交互的情况下,服务器无法预知的.
    而你说的"服务器端叫Extender",无非是在交互状态下去通知服务器confirm的分支.

    LZ所说"支持调用服务器端代码分支的confirm,怎么办?这时候你就真的需要一个与服务器端Control对应的客户端Control了", 认为只要与服务器产生交互即可.如LZ所说的服务器控件,以及XMLHttpRequest
      回复  引用  查看    
  4. #4楼[楼主] Cat Chen      2008-10-27 10:30
    @坐断东南 笑煞之!!
    Extender只是Behavior的封装,自然也不能实现分支。我所说的Control,是指如同一般纯服务器端Control一样能够影响PostBack数据的Control,这样才能进行分支选择。
      回复  引用  查看    
  5. #5楼 救世主[未注册用户]2008-10-27 10:32
    像楼主这样写的自己的心得体会的人现在在博客园里实在是少了又少,大部分都是混个MVP然后装B的人,支持楼主.
      回复  引用    
  6. #6楼[楼主] Cat Chen      2008-10-27 10:45
    @救世主
    谢谢!
      回复  引用  查看    
  7. #7楼 代码乱了      2008-10-27 12:01
    分析得很深入
      回复  引用  查看    
  8. #8楼 代码乱了      2008-10-27 12:36
    期待下一篇
      回复  引用  查看    
  9. #9楼 JimLiu      2008-10-27 17:57
    @Cat Chen
    呵呵,也许说成“Behavior能给现有组件添加客户端行为,这一点正如它的名字;但不能对服务端的流程作出响应、反馈,更不用说是影响”比较容易理解。
    对于@要有好的心情的看法,我认为在Behavior中发起XMLHttpRequest,不应该算是“对服务端流程产生影响”,而应该是开启了另一个服务端流程。
    我觉得Extender这个名字更能说明Behavior, Extender的作用——扩展者,扩展现有的组件,而不是去影响它,修改它。
      回复  引用  查看    
  10. #10楼[楼主] Cat Chen      2008-10-27 18:01
    @JimLiu
    Exactly!
      回复  引用  查看    
  11. #11楼 要有好的心情      2008-10-27 21:29
    个人觉得 客户端的Behavior和服务器的ExtenderControl既是无奈之举,又是精巧的设计。想当初,估计asp.net team也没太重视客户端的脚本,虽然也有doPoskBack、验证等, 当时没让WebControl实现IScriptControl接口 肠子都悔青了。世上没有卖后悔药的呀,还好牛人毕竟设计能力强,ExtenderControl控件轻松解决了这个问题,所以说没有解决不了困难,而且解决的非常漂亮。

    ExtenderControl控件用于增强标准WebControl的客户端功能,因为已经写好的WebControl不能再动了,要按原来的样子Render,但可以附加事件,所以ExtenderControl控件的真实功能只是在Render时,生成客户端脚本。因此ExtenderControl控件一般不会单独使用,必须作为一个标准WebControl的附加控件,这通过指定ExtenderControl控件的TargetControlID来实现。而Sys.UI.Behavior是纯粹的客户端对象(姑且这样说),没有服务器端的ExtenderControl控件也可以照样使用。但ExtenderControl控件会减少一些机械劳动,所以对已有的标准WebControl这种方案可以说非常不错了。

    如果要写全新的WebControl,同时还要增加客户端行为,可以从ScriptControl开始,而不是WebControl。

    “而改变原有组件的逻辑在客户端往往是不可行的”:都已经写好了,怎么能重新来过呀,而且也有兼容性问题。

    “你无法扩展这个对象的类型,也无法为它加上新的属性与方法”:其实我们的目的不是要扩展DOM元素的属性,添加新属性,这个由W3C控制,也不是扩展DOM元素的功能,有了js我们能实现很多功能。我们只是要扩展标准WebControl的功能,这是asp.net WebForms模型独有的现象,现在不用也提供了MVC模型吗。

    “我们可以把Behavior看作一种折衷了的decorator pattern。在decorator pattern中,decorator也继承自组件.....”:我不太懂decorator pattern,不过这怎么也不能算是折衷了的decorator 吧。

    “Behavior本身不是一个浏览器内部的组件,它无法继承自input的基类”:感觉lz还是没太搞清楚Sys.UI.Behavior的目的呢,他的目的根本就不是要“继承自input的基类”,否则要ScriptControl干什么。 也许我理解的不对。

    “这使得Behavior可以并联起来”:并联起来原因不在于你所说的吧。有时间我们私聊。
      回复  引用  查看    
  12. #12楼[楼主] Cat Chen      2008-10-27 23:31
    @要有好的心情
    我是尝试说明Behavior如此设计的前因后果,是因为有这些难题所有Behavior才如此设计,而非Behavior设计来解决这些难题。
      回复  引用  查看