一个小的WEB项目中的实现方法讨论 - 又一篇[原创]

07-03-15 s79
                   

一个小的WEB项目中的实现方法讨论是2004年10月的一篇帖子,给了我们这些初学者一个很好的提示。

该方法使用command模式,以一个入口servlet作为controller,然后根据request的参数servicetarget决定调用哪个modle做处理,然后把结果返回到viewtarget。是一个基本的MVC实现。其controller会根据service参数,使用类的动态加载机制Service service=(Service)serviceClass.newInstance()来实现类似脚本语言的eval()功能来构建动态变量。

深入的分析一下,就会发现以下几个问题:

问题1:随着请求的增加,类的动态加载会造成很大的性能开销,所以这里有必要采取对象池来缓存创建的对象。但这就造成了代码不够简明,加大了学习难度。

问题2:变量target是传给controller供回调时用的,目的是为了把所有页面的跳转都交给controller处理,但一旦modle中的处理出现异常,将不能再返回target指定的页面,这就需要modle给出一个新的返回页面,于是modle层偶尔也要参与页面跳转。

这些问题总觉让人觉得如鲠在喉,虽然做到了MVC的实现,但却不够优雅,甚至牵强。于是我参考了一些资料,结合了以前做c/s软件的思路,本着简化设计的原则,想了以下这种模式来实现系统的设计:

--------------------------------------------------

B/S=统一C的C/S,不要忽视C的处理能力

--------------------------------------------------

1:概念

我们都熟知的MVC理论中,M就是处理业务逻辑的,V就是控制表现的,C就是控制页面跳转的。

一定要这样实现吗?

如果全部都是动态页面,这样的实现是可行和严密的,但如果混杂了静态页面,里边的<a>元素直接定义一个超链接,这样V层直接控制了页面的流向。这就显得不够严密。

这里介绍的概念是:

M=只是处理业务逻辑,并以字符串方式输出最终处理结果。(因为M是V通过script经C验证后调用的,M返回的结果直接就会到了V。)

V=只是处理页面跳转,全部采用静态页面设计,以Ajax或动态加载脚本技术请求V并接收返回值,以脚本在页面处理得到的数据。

C=只是控制V访问M的权限,实现V到M的验证和连接。

更严格的分层如下:

Server层-C+M

Client层-V+Script

这样系统处理的流程就是:

请求V-C-M,返回M-V。

2:实现

以一个Filter充当Controller,将*.do的请求以url方式转向到其他servlet或jsp处理。转向前可做权限检测。

接受转向的servlet全部继承一个接口,此接口实现2种功能,1是verify请求来源是否为Controller,2是execute,并直接将处理结果out.print出来。

V直接得到处理后的结果,并以script表示。

3:优点

A.逻辑严密

所有页面跳转都是V控制的,C只是验证是否有权限访问后台的一个控制器,M根据请求参数做业务处理,并给回返回值。

B.资源耗费最小化

B.1:服务器资源耗费最小化

传统的MVC是通过把数据层层封装,传输到前台以<%=Object.getValue()%>显示,这种MVC是直接取到数据,并以javascript结合DOM在前台动态生成。

传统MVC执行数据库的R操作,服务器的流程是V-C-M-C-V,其中新建数据传输用的临时对象n个。

这种MVC的流程是V-C-M,新建数据传输用的临时对象0个,没有后边的C-V过程。(因为M直接输出结果字符串给V了,V请求的同时就得到了这个结果。M-V的过程不需要后台参与,V的合成是在客户端完成的。)

这样因为M和C都是常驻内存的servlet(C是filter,严格来说不能算是servlet,但也是常驻内存的,jsp本质就是servlet),所以只需要注意线程安全就足够了,线程池一般的容器也都提供,几乎不必使用对象池,对待频繁访问的数据,可以由受到频繁访问的M生成静态文本文件实现cache,节约了大量资源,也简化了编程复杂度。

B.2:网络传输耗费最小化

如果采用Ajax技术,可以实现只传输必要的更新数据,节约大量数据流量。详情请参考Ajax相关资料。

C.服务器平台无关性

因为html和javascript是平台无关的,而对后台的请求得到的都是字符串,我们就可以随意选择后台的运行环境,而不必重新设计V。方便的从jsp-asp-php或其他任何语言中选择。

D.降低学习难度

用这种模式开发WEB站点,jsp/asp/php/等语言,前台编写的难易程度都变成写静态页面的难度了,后台程序员只需要关注自己的后台性能即可。

不论前台还是后台的人员,什么Taglib,什么EL的都不用去看了,甚至连jsp都不需要去学习,只要前台会一点javascript就可以熟练的操作DOM,实现动态更新页面。而后台的程序员也只要控制好各个servlet的访问权限就足够。

E.人员分工合理化

通过使用模板字符替换的技术,可以实现由脚本将数据锚点的内容动态替换成从后台取到的数据,前台美工只需要知道调用哪个url会得到什么结果就可以了,让美工去处理页面的跳转和错误提示信息,这样美工和后台终于可以完全的分割开来。

--------------------------------------------------

正如本文所说,这个构想只是“小”WEB项目的实现方法。大型的企业级分布式应用,我还没接触过。不敢妄加想象。

传统的“推”模式只适合在以前智能终端的年代,那时的客户端处理能力有限,所以是胖Server,瘦Client。

后来客户机的性能逐渐有所提高了,但在WEB方面,Browser的功能还比较单一,所以“推”习惯了的依然在推,CGI把处理好的html推给Browser,Browser只要下载处理好的html,只负责显示就可以了。

到了现在,Browser方面脚本语言的应用早已成熟,我们可以用“拉”模式,仅取出需要的数据,然后在前台合成。实现瘦Server,胖Client,减轻Server的负担。

——不要忽视C的处理能力:C已经吃够现成饭了,不需要S做好了饭菜只管吃了,S只要提供原材料,让C吃自助餐,C会吃的更好!

以上所说由M返回的字符串,可以是一段script代码,也可以是一个xml。具体应用看需求了,如果不是必须,不推荐用xml,因为xml最终也要还原成元数据,倒不如直接写入script来的痛快。构建RSS的时候只需要单独写一个生成RSS的servlet即可。

感谢您看到这里,我刚步入WEB开发行业不久,所学有限,上面的实现方法虽已构思了一段日子,但仍难免考虑不周之处,希望各位朋友看到后能及时斧正。

我喜欢来J道,因为我喜欢Taoism,中国的国教其实是道教,道学是博大精深的。我也敬佩Banq大哥的精神,鄙视一些只是提出批判而没有给出我们其他同道任何有益提示的所谓高手。

祝大家早日得道~

[该贴被s79于2007年03月15日 03:30修改过]

                   

1
s79
2007-03-15 03:40

更正:

上边说的C/S和B/S不够严密,因为B/S只能通过Client向Server发送请求,才会得到回应,Server永远是被动的。而真正的C/S服务器可以主动发送信息给客户端的。以这个打比喻的目的是:强调多让Client做些事情,Server只是做他必须做的。

同样“推”和“拉”的说法也不够严密,所有这些说法都应该理解为对“数据”的动作上。

(QQ:396686)

[该贴被s79于2007年03月16日 06:52修改过]

banq
2007-03-15 16:19

鼓励

这是一个比较轻松、紧凑的“小”WEB项目的实现方法,如果多出一个专门的Model业务层,或者提供以后M复杂以后可以单独开辟一个层的接口,那么拓展性就更好了。

s79
2007-03-17 09:07

有些问题想不通,又来请教了。

用上述的办法,就不必使用jsp文件,只剩下静态html的V。Browser请求到达M,M把处理结果返回Browser。Browser用自己的方式去动态处理并合成V,M和V的接口就是AjaxCall之类的技术,那么是否还有必要引入C层呢?

问题1:引入C,可以使用一个Servlet使用Command模式动态加载Class模块M来处理请求,保证了Servlet的唯一性。但动态加载是耗费资源的,不知道其他MVC框架怎么解决的呢?使用了Cache还是避免了动态加载?如果功能模块暂时不需要变更,是否可以import多个class,并根据参数用if来替代动态加载?

问题2:不引入C,只能每个模块M写成一个Servlet,但在业务逐渐复杂的时候,会出现数十个Servlet模块M,这样是否浪费资源?这种浪费和动态加载那个更有害?

关于各种模式举例对比如下,上述问题1和问题2分别对应以下的模式3和模式4,其中带下划线的过程在每个模式中是相同的:

假设有一个餐馆Server,来了个客人Browser,他们走到餐桌(一个页面)前,看了看菜谱,开始点菜(请求数据)。其中有三种角色MVC。

M-厨师,根据要求作菜。

V-传菜员,把菜配上餐具供客人Browser食用。

C-吧员,协调厨师和传菜员。

该餐馆在不同的经营模式下发生的事件分别是:

经营模式1:(Mode1)

传菜员V(JSP)听到客人喊的菜名(request)后,告诉(request传递)厨师M(class)做好饭菜,厨师做好后交给(结果传递)传菜员V,传菜员V将其配上餐具(将数据嵌入html),然后送到客人的餐桌,客人开吃(浏览)。(吧员C没有出现)

经营模式2:(Mode2)

吧员C(Servlet)听到客人喊的菜名(request)后,告诉(request传递)厨师M(Class)做好饭菜,厨师做好后交给(结果传递)吧员C,吧员C叫来(结果传递)传菜员V,传菜员V将其配上餐具(将数据嵌入html),然后送到客人的餐桌,客人开吃(浏览)。(三种角色都出现)

经营模式3:

吧员C(Servlet)听到客人喊的菜名(request)后,告诉(request传递)厨师M(Class)做好饭菜,厨师做好后交给(结果传递)客人,客人根据餐桌上的指示文字(Ajax)自己去找合适的餐具,开吃(浏览)。(V没有出现)

经营模式4:

厨师M(Servlet)听到客人喊的菜名(request)后,做好饭菜交给(结果传递)客人,客人根据餐桌上的指示文字(Ajax)自己去找餐具,开吃(浏览)。(V和C都没有出现)

banq
2007-03-19 09:51

你之所以存在是否"引入Controller"问题,因为你原来设计中没有专门的业务层,,所以我建议你还是设计一个Model业务层,因为不可能所有业务都放在Model中,按照Evans DDD,业务层分领域层和服务层。

Controller是Mediator模式实现,是封装前后台交互中介点,只要你存在后台业务层,就需要一个Controller来介入封装前后通讯。

struts等都是使用动态加载实现的,通过缓存提升性能,所以,你可以引入struts这个MVC框架,这与AJAX并不矛盾,见我另外一个帖子的回复:

http://www.jdon.com/jive/thread.jsp?forum=61&thread=31159

至于你的四个模式都是可行的,但是如果加上系统的可维护性和可拓展性这个软件设计的标准的化,经营模式2的三种角色都出现是比较好的方案。

当然,有人会说,在一个小系统中,C出现好像挺麻烦,能不能让其出现,但是在小系统时,编程人员感觉不到C的出现,但是以后需要拓展,可以找到这个C呢?

回答可以的,比如我设计的JdonFramework中,C我就隐去了,只要进行jdonframework.xml中models方面的配置就可以,无需编码,如果以后你的C比较复杂,还是可以自己写一个Struts的Action作为C的。

4Go 1 2 3 4 下一页