通过MVC模式将Web视图和逻辑代码分离

简介: 本文为原创,如需转载,请注明作者和出处,谢谢!     MVC模式(Model-View-Controller)常被用在Web程序中。如Struts框架就是一个基于MVC模式的Web框架。
本文为原创,如需转载,请注明作者和出处,谢谢!

    MVC模式(Model-View-Controller)常被用在Web程序中。如Struts框架就是一个基于MVC模式的Web框架。所谓MVC模式,就是将视图(也就是客户端代码,包括htmljavascript等)和模型(和数据库及业务逻辑相关的Java代码)分开。并通过控制器将两者联系起来。这样做的好处可以使客户端开发人员和服务端开发人员的工作尽量分开,以使他们之间的干扰降到最低。

    虽然象 Struts 这样的框架可以很好地 Model View 分离。但是对于客户端的代码,仍然存在着一定的视图和逻辑混合的现象。如下面的代码所示:

<! DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" >
< html >
    
< head >
        
< title ></ title >
        
< meta  http-equiv ="Content-Type"  content ="text/html; charset=UTF-8" >
        
< script  type ="text/javascript"   >
        
function  fun1(obj) {  }
        
function  fun2() { }
        
</ script >         
    
</ head >
    
< body >
        
< input  type ="button"  value ="按钮1 "  onclick ="fun1(this)" />
        
< input  type ="button"  value ="按钮2 "  onclick ="fun2()" />
         
    
</ body >  
</ html >

    从上面的代码可以看出,html代码和javascript代码都混在了同一个html文件中。在一般情况下,客户端的界面是由美工设计的,而对于javascript代码,美工一般是不参与实现的。这些代码一般也应属于业务逻辑代码的一部分,虽然它们都在客户端运行,但可能也会处理一定的业务逻辑,如验证数据的正确性。尤其在AJAX应用中,在客户端还会通过http协议从服务端获取数据。这样就和业务逻辑绑定得更紧了。因此,如果将用于描述界面的html和用于处理业务逻辑的javascript(渲染界面的javascript除外)混在一起,非常不利于团队中负责这两方面的人员进行协调。

    最好的可能就是将这些javascript代码从html代码中分离。也许有很多人马上就会想到,将这些javascript放到.js文件中,然后使用<script>标签引用一下就ok了。代码如下:

fun.js文件

function fun1(obj) { ... }

function fun2() {... }


index.html文件

<! DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" >
< html >
    
< head >
        
< title ></ title >
        
< meta  http-equiv ="Content-Type"  content ="text/html; charset=UTF-8" >
< script  type ="text/javascript"  src ="fun.js" >
</ script >
    
</ head >
    
< body >
        
< input  type ="button"  value ="按钮1 "  onclick ="fun1(this)" />
        
< input  type ="button"  value ="按钮2 "  onclick ="fun2()" />
         
    
</ body >  
</ html >

    虽然上面的代码从某种程度上达到了视图和逻辑分离的效果。但仍然有着一定的联系。我们可以看到,两个<input>标签的onclick事件不还是引用着fun1fun2函数吗!其实美工人员是不关心这两个函数到底是做什么的,甚至并不需要知道它们的存在。那么是否有更高的方法呢?答案当然是肯定的,就是通过动态的方式指定onclick事件,而这一切美工人员是完全没有感觉的。

    我们在文章的开头提到了MVC模式。其实在客户端也存在着一个MVC体系结构。我们可以将视图(V)看成是描述界面的htmlcssjavascript代码,而模型(M)可以看成是处理业务逻辑的javascript代码,而控制器(C)就是将这两类代码连接起来的代码(一般也是javascript代码)。

    在本文中给出了一个小例子来演示一下如何通过动态的方法将VM分离。这个例子是通过<div>实现的10个小方块,点击其中的某一个,会将相应的数字追加到下方的文本框架中,并且加入了一些用javascript实现的效果,如以一定间隔随机更新方块和数字的颜色,直接在文本框中输入数字后,相应的文本框和数字的颜色也会随机发生变化。界面效果如图1所示。



                                         图1

    下面先来实现View。先看看如下的代码:

 numberKeys.html

   <! DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" >
  
< html >
      
< head >
          
< title ></ title >
          
< meta  http-equiv ="Content-Type"  content ="text/html; charset=UTF-8" >
          
< link   rel ="stylesheet"   type ="text/css"  href ="my.css" />
          
< script  type ="text/javascript"  src ="addevents.js" >
          
</ script >

      
</ head >
      
< body >
          
< input  id  ="changeColor"  type ="button"  value ="开始变换颜色"   />
          
< br />< br />

          
< div  id  ="nubmerKeys"  class ="numberKeys"    >     

          
</ div >  
          
< br />< br />
          
< br />< br />
          
< input  id ="numbers"  type ="text"  size ="80" />

      
</ body >  
  
</ html >     

    从上面的代码可以看出,除了一些html代码外,没有任何和业务逻辑有关的javascript代码。但使用<script>引用了一个叫addevents.js的文件。在这个文件中将为这个程序添加相应的逻辑代码,也就是说,这个文件相当于MVC中的MC

    动作装载事件是通过 window onload 事件完成的,代码如下:

window.onload  =  onLoad;   //  为onload指定事件函数

function  onLoad()
{
    
var  text  =  document.getElementById( " numbers " );
    
if (text)
    {      
        text.onkeyup 
=  onKeyup;   //  为文本框指定onkeyup事件
    }
    
var  button  =  document.getElementById( " changeColor " );
    
if (button)
    {
        button.onclick 
=  stopAndStartTimer;   //  为按钮指定onclick事件
    }  
    addButton();  
//  用于在<div>中加入10个<div>标签作为小方块,并指定onclick事件
}

    下面先来看一下 addButton 函数是如何实现的,代码如下:

function  addButton()
{
    
var  div  =  getNumberKeysDiv();   //  获得id为nubmerKeys的<div>标签

    
try
    {
        
if (div)
        {
            
//  删除<div>中的所有子元素
             for ( var  i  =  div.childNodes.length  -   1 ; i  >=   0 ; i -- )
            {
                
try
                {   
                    div.removeChild(div.childNodes[i]);
                }
                
catch (e)
                {                    
                }            
            } 
            
//  为<div>标签加10个子<div>标签
             for ( var  i  =   0 ; i  <   10 ; i ++
            {
                
var  button  =  document.createElement( " div " ); 
            
                button.className 
=   " button " ;        
                button.style.left 
=  i  *   61   +   " px " ;
                button.style.backgroundColor 
=  getRandomColor();
                button.style.border
= " solid 1px " ;
                button.style.textAlign 
=   " center " ;
                button.style.lineHeight 
=   " 50px " ;      
                button.style.color 
=  getRandomColor(); 
                        
                button.onclick 
=  buttonOnClick;
            
                div.appendChild(button);               
                button.innerHTML  
= " <b> "   + +   " </b> " ;                
            }            
        }
    } 
    
catch (e)
    { 
    }            
}

    addButton函数的基本实现原理是先将<div>中的所有元素删除,再向其中加入10<div>标签。在addButton函数中有几个关键的函数需要讲解一下。

getNumberKeysDiv函数

    这个函数用于获得叫 numberKeys <div> 标签,实现代码如下:

function  getNumberKeysDiv()
{
    
var  divs  =  document.getElementsByTagName( " div " );
    
    
if (divs)
    {
        
for ( var  i  =   0 ; i  <  divs.length; i ++ )
        {
            
var  div  =  divs[i];
            
            
if (div.className.toString().indexOf( " numberKeys " 0 >   - 1 )
            {  
                
return  div;                
            }
        }
    }
}

    这个函数并不是直接通过<div>id找到这个<div>,而是通过<div>class属性,这样可能更灵活,因为如果通过id<div>,就必须要求美工必须将这个<div>命名为numberKeys,而如果通过查找包含numberKeysclass属性的<div>会对美工的限制更少。因为,只有这个<div>才会使用css中的numberKeys

getRandomColor方法

这个方法获得了一个随机的演示,返回了字符串类型,格式 #FFFFFF 。实现代码如下:

function  getRandomArbitary(min, max)
{    
    
return  Math.round(Math.random()  *  (max  -  min)  +  min);
}
function  getRandomColor()
{
    
var  red  =  getRandomArbitary( 0 255 ).toString( 16 );    
    
var  green  =  getRandomArbitary( 0 255 ).toString( 16 );
    
var  blue  =  getRandomArbitary( 0 255 ).toString( 16 );
    
if (red.length  ==   1 ) red  =   " 0 "   +  red;
    
if (green.length  ==   1 ) green  =   " 0 "   +  green;
    
if (blue.length  ==   1 ) blue  =   " 0 "   +  blue;
    
    
return   " # "   +  red.toString( 16 +  green.toString( 16 +  blue.toString( 16 );
}

    getRandomColor通过getRandomArbitary函数获得了三个十进制的随机数(0-255),并将其转换为16进制,并返回最终结果。

buttonOnClick函数

这个函数是<div>标签的onclick事件函数,实现代码如下:

function  buttonOnClick()
{
    
var  text  =  document.getElementById( " numbers " );    
    
if ( typeof   this .innerText  ==   " #ff0000 " )
        text.value 
=  text.value  +   this .textContent;    
    
else  
        text.value 
=  text.value  +   this .innerText;    
    
this .style.backgroundColor  =  getRandomColor();              
    
this .style.color  =   getRandomColor(); 
    
}

    这个函数实现很简单,它的功能是将相应<div>标签中的数字追加到numbers文本框中。只是考虑了firefoxie的不同。在firefox中,获得<div>中的文本要使用textContext,而在ie中要使用innerText。最后再将当前点击的<div>和数字的颜色再次变换。

    到现在为止,还有两个事件函数代码没有给出,这两个事件函数是onKeyupstopAndStartTimer

    onKeyup函数

    numbers 文本框输入一个字符后,发生这个事件,实现代码如下:

function  onKeyup()
{
    
var  value  =   this .value;
    
if (value.length  ==   0 return ;
    
var  i  =  value.toString().substr(value.length  -   1 1 ); 
    
if (isNaN(i)  ==   false )
    {    
        
var  div  =  getNumberKeysDiv();    
        
if (div)
        {
            
var  button  =  div.childNodes[i];
            button.style.backgroundColor 
=  getRandomColor();              
            button.style.color 
=   getRandomColor(); 
         
        }
    }
}

    这个函数的实现代码也很简单。只是根据用户在文本框中输入的数字来找到相应的<div>标签,并再次随机变换<div>背景和数字的颜色。

stopAndStartTimer函数

这个函数是用来控制定时器的,如果启动定时器,浏览器会每隔3秒重新使<div>随机变化一次颜色。实现代码如下:


var  time;

function  stopAndStartTimer()
{
    
if ( this .value.toString().indexOf( " 停止 " , 0 >   - 1 )
    {
        
this .value  =   " 开始变换颜色 " ;
        clearInterval(time);            
    }
    
else
    {
        
this .value  =   " 停止变换颜色 " ;
        time 
=  setInterval(onLoad,  3000 );
    }
}

    从上面给出的代码可以看出,在View层,除了<script>引用了一个javascript文件外,并未涉及任何和逻辑有关的代码。而设计界面的美工也并不知道开发人员会为<div>及其他的按钮和文本框添加什么动作。而美工要做的是调整和界面有关的东西,如颜色,位置,分割等。只要使用<script>引用了这个js文件,就可以很容易加入相应的动作,而要将这些动作去掉,删除或注释掉这个<script>标签即可。

    尤其要提一下<div>标签,美工在设计界面时可以向这个<div>标签添加任何子元素。而在加入addevents.js后,程序会自动将由美工向<div>标签中的加入的内容都删除,而加入由业务逻辑需要的元素。这样美工用来设计界面的元素就不会影响开发人员需要加入的和业务逻辑有关的元素了。

    根据上面的代码不难看出,numberKeys.html属于视图层,所有的事件函数属性模型层,而其他的javascript代码都属性控制器(Controller)




国内最棒的Google Android技术社区(eoeandroid),欢迎访问!

《银河系列原创教程》发布

《Java Web开发速学宝典》出版,欢迎定购

目录
相关文章
|
5月前
|
设计模式 存储 前端开发
MVC(Model-View-Controller)是一种软件设计模式,用于将应用程序的输入逻辑、业务逻辑和用户界面逻辑分离
【6月更文挑战第17天】**MVC模式**是软件设计模式,用于分离输入逻辑、业务逻辑和用户界面。模型处理数据和业务,视图展示数据,控制器协调两者响应用户请求。优点包括:关注点分离、提高开发效率、简化测试、支持多视图及便于大型项目管理。
51 3
|
6月前
|
前端开发 JavaScript Java
springboot 出现 Cannot resolve MVC View ‘index‘ 问题解决办法,前后端不分离项目前端文件存放位置,已经如何访问
springboot 出现 Cannot resolve MVC View ‘index‘ 问题解决办法,前后端不分离项目前端文件存放位置,已经如何访问
706 1
|
前端开发 JavaScript .NET
MVC项目开发中那些用到的知识点(MvcContrib分离ASP.NET MVC项目)
前言 在http://www.cnblogs.com/aehyok/archive/2013/04/07/3006438.html这篇随笔中,我简单的介绍了,asp.net mvc3.0中自带的Areas的使用。
1011 0
MVC3中把Control中的业务逻辑层BLL单独分离出来时引用注意事项!
在BLL里注意声明过程方法为public类型,不然无法使用类中定义的过程方法!学习交流群:364976091
556 0
|
Web App开发 前端开发 .NET
为ASP.NET MVC RC分离Controllers-Views项目后添加“脚手架”功能(一)(已添加MVC2.0及MVC3.0更新)
连续忙了好几个月,好久没有写东西了,最近稍微有点空,空闲的时候回到了对ASP.NET MVC RC(以下简称MVC RC)的研究上来。MVC RC的“脚手架(Scaffold)”功能可以说为MVC RC的开发如虎添翼,不过应用到真实的开发环境中似乎存在一些遗憾的地方:很多时候我们并不希望把Models、Views和Controllers放在同一个项目里面,而是把它们分离到不同的项目,然后由一个项目(比如Views)统一引用其他所有的项目程序集。
957 0
|
3月前
|
开发框架 前端开发 .NET
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
ASP.NET MVC WebApi 接口返回 JOSN 日期格式化 date format
47 0
|
6月前
|
开发框架 前端开发 .NET
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
ASP.NET CORE 3.1 MVC“指定的网络名不再可用\企图在不存在的网络连接上进行操作”的问题解决过程
189 0