求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
  
 
 
 
通过MVC模式将Web视图和逻辑代码分离
 

2010-06-11 作者:银河使者 来源:银河使者的blog

 

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

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

<!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

动作装载事件是通过windowonload事件完成的,代码如下:
 
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>" +i + "</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)



如何向妻子解释OOD
OOAD与UML笔记
UML类图与类的关系详解
UML统一建模语言初学
总结一下领域模型的验证
基于 UML 的业务建模


面向对象的分析设计
基于UML的面向对象分析设计
UML + 嵌入式系统分析设计
关系数据库面向OOAD设计
业务建模与业务架构
使用用例进行需求管理


某航空IT部门 业务分析与业务建模
联想 业务需求分析与建模
北京航管科技 EA工具与架构设计
使用EA和UML进行嵌入式系统分析
全球最大的茶业集团 UML系统分析
华为 基于EA的嵌入式系统建模
水资源服务商 基于EA进行UML建模
更多...