原文地址:http://www.larryullman.com/2009/10/15/understanding-mvc-part-3/

全系列INDEX

翻译:shadowmydx 转帖请注明

在这个系列的第一部分,我讨论了MVC设计模式的各个部分以及它们的具体表现。在第二部分,我谈了些web开发框架的共同约定。那两个部分的文章都是这篇文章的基础,是时候讲讲真正的代码了。个人以为,理解MVC这个词是啥意思、理论上了解MVC设计模式的大概流程以及遵守框架的命名规则并不难,但是如果你就这样开始开发一个项目,你将会在实际编码中迅速的迷失你的方向,最后变得一团糟。

问题就出在MVC的运作方式和你迄今为止做的工作基本完全相反。如果你是个PHP程序员,正在开发一个列出所有部门(还是那个雇员-部门应用)的页面,你很可能会这么来写这个php程序:

  1. 新建一个html页面,将header文件包含进去
  2. 连接进数据库
  3. 查询数据库
  4. 确认收到了查询结果
  5. 通过一个循环检索返回的查询结果
  6. 用某种html的方式将查询结果打印出来,比如table或者list
  7. 关闭数据库连接(也可能不关)
  8. 完成整个html页面

这就是我们在哪怕如此简单的页面中需要做的东西。即使你使用了包含文件来处理数据库的连接以及引入模板文件来布局HTML,我们仍然需要处理非常多的逻辑。如果你通过在URL中传递部门ID的方式将那些展示部门详细信息的功能做到了另一个页面中,那个页面恐怕也需要增加一些数据确认的步奏。如果你有一个页面同时展示并处理了某个表单,比如增加一个雇员,那还需要你增加非常,非常多的逻辑。并不是说这种开发方式有什么不对的地方,但是这种方式完完全全站在了MVC设计模式的反面。你可以想象MVC式的开发是一个金字塔,模型在最底层,然后到控制器,最后是很小一部分的视图。

作为编程的一个原则,视图只需要做很少的一点点事就可以了,大多数情况下就打印打印变量的值。视图中最复杂的部分最好也只是通过一个条件判断语句来确认一个变量的值是否存在并打印,或者使用一个循环来打印数组中的全部元素。视图仅仅产生用户能看到的东西,就这么多了,没别的了。控制器中有更多的逻辑,例如应答用户的动作,并且扮演位于模型和视图之间的接口角色。但是这里会有一些常犯的错误,那就是把应该位于模型中的代码放在了控制器中。一个MVC设计模式的原则就是“肥肥的模型,廋廋的控制器”(shadowmydx:原文是fat model,thin controller,大家采那个意思就好)。这意味着你应该把更多的代码压倒应用的地基中去(那就是金字塔的底座 —— 模型)。我曾经看过一篇关于MVC的文章,作者认为模型的功能在于维持http请求的状态。我认为这是个非常棒的描述,因为维持http请求状态就包含了储存数据和处理数据。存储数据,就好比在部门模型中通过不同的请求对数据进行增加、修改和检索,而处理数据就好比在联系模型中获取用户提供的数据,证实后发一个email。(shadowmydx:原文中储存数据为stored data,处理数据为processed data that doesn't necessarily get stored)

那么这叫现实的代码中究竟意味着什么?首先需要明确,如果你的视图文件中包含的语句功能超过了echo/print,还有一点控制结构的话,你可能已经在犯错误了。如果你在视图中创建了一个新变量,这也意味着你可能犯错了。实际上,应该只有很少的PHP代码在视图中运行,哪怕你使用循环打印数组也应该选用下面这种形式,把重点放在html上面来。

<?php foreach($departments as $n=>$dept): ?>
<li><?php echo $dept->something; ?></li>
<?php endforeach; ?>

与之相反的是我们在通常的PHP程序中采用的方式,重点在于PHP代码:

<?php
foreach($departments as $n=>$dept) {
echo '<li>' . $dept->something . '</li>';
}
?>

现在,该讲讲控制器中的代码了。控制器里的代码主要任务是控制行为,例如对功能进行分配以及对用户的输入做出反应。控制器是一些类,拥有完成实际工作的一些方法。这些工作主要包括检索特定的模型(例如从表中获取一个记录),调用一个模型来完成插入、修改或者删除的任务,然后调用一个视图来展示这些结果。模型的任务,就是剩下的全部部分,例如执行某个查询语句,验证数据诸如此类。不过,在框架中,大部分关于模型的代码都已经内置了,所以你可以调用一些模型自己没有定义的方法,例如save或者delete(这些方法是从框架的model类中继承而来的)。

让我们回到那个雇员-部门的例子中来。不妨假设现在你想要有一个页面展示出所有部门以及部门领导的名字。在部门模型中,储存着部门领导的雇员ID,那么,控制器就可以通过调用模型来获取全部的部门(下面的是Yii框架的语法):

$dept = Departments::model()->findAll();

$depts变量会传递到相应视图中来打印给用户。这就是Yii使用MVC的基本方式,当然,部门模型并没有实际定义一个findAll()的方法,这是继承过来的。

现在你需要找到每个部门临到的名字来让视图显示。为了找到那个人的名字,你需要获得一个同部门模型中储存ID一致的雇员模型。使用Yii的话,代码就是:

$emp = Employees::model()->findByPk($dept->departmentHeadId);

这句代码的意思是,你想要一个主键值和当前部门模型departmentHeadId值一致雇员模型。(shadowmydx:findByPk中,Pk的意思是primary key,也就是主键)

一个你绝对不能做的事就是在视图中写类似的查询语句,这将是一场灾难。也许现在的你更倾向于把这些代码放到控制器中,为了达成这个目的,你需要一个循环来检索部门模型,获得deparmentHeadId,然后查询主键与之一致的employee模型,最后将其与相应的部门关联起来。

需要明确的是,Yii以及其他大部分框架都支持连接不同的模型。在Yii中,你可以这么做(当然需要你先弄清模型间的关联):

$departments = Departments::model()->with('employees')->findAll();

但是,如果你的框架没有这个功能,或者两个模型之间并没有可供联接的关联,你可以增加下面这个方法到部门模型之中:

function getDepartmentHead() {
$emp = Employees::model()->findByPk($this->departmentHeadId);
if ($emp) {
return "$emp->lastName, $emp->firstName";
} else {
return 'unknown';
}
}

在定义了这个方法以后,你就不需要在控制器里增加代码了。你可以在视图中用一个foreach循环来完成这个工作:

echo $dept->getDepartmentHead;

 

还有个重要的例子,假如你想用LastName,FirstName这种格式打印雇员的名字,又或者你有更复杂的输出格式,你为此写的方法应该定义在雇员模型中,然后你就可以在任何一个需要使用这个模型的地方调用这个方法,例如什么展示所有雇员、展示某个雇员的具体信息以及展示部门领导等等部分。一切都是为了代码的复用性。

好了,终于结束了。有问题可以去原作者的页面留言

【原创翻译】认识MVC设计模式:web应用开发的基础(实际编码篇)的更多相关文章

  1. 基于Spring MVC的Web应用开发(三) - Resources

    基于Spring MVC的Web应用开发(3) - Resources 上一篇介绍了在基于Spring MVC的Web项目中加入日志,本文介绍Spring MVC如何处理资源文件. 注意到本项目的we ...

  2. WEB前端开发CSS基础样式全面总结

    Web前端开发css基础样式全面总结 颜色和单位的使用 颜色 用颜色的名字表示颜色,比如:red 用16进制表示演示 比如:#FF0000 用rgb数值表示颜色,rgb(红,绿,蓝),每个值都在0-2 ...

  3. 云小课|DGC数据开发之基础入门篇

    阅识风云是华为云信息大咖,擅长将复杂信息多元化呈现,其出品的一张图(云图说).深入浅出的博文(云小课)或短视频(云视厅)总有一款能让您快速上手华为云.更多精彩内容请单击此处. 摘要:欢迎来到DGC数据 ...

  4. [翻译]Spring MVC RESTFul Web Service CRUD 例子

    Spring MVC RESTFul Web Service CRUD 例子 本文主要翻译自:http://memorynotfound.com/spring-mvc-restful-web-serv ...

  5. 移动Web应用开发入门指南——交互篇

    交互篇 从PC到移动端,视觉和交互是用户能直接感受到的差异.在视觉篇中已经提到,移动设备的物理属性一部分影响到视觉,另外一些部分将影响到交互.那么,移动设备影响交互的物理属性都有哪些变化呢?对于这个问 ...

  6. (数据科学学习手札114)Python+Dash快速web应用开发——上传下载篇

    本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...

  7. Java高级架构师(一)第13节:Spring MVC实现Web层开发

    package com.sishuok.architecture1.customermgr.web; import org.springframework.beans.factory.annotati ...

  8. 移动Web应用开发入门指南——兼容篇

    兼容篇 兼容篇是我最想写的一部分,在这之前也总结过很多关于移动开发的兼容问题与解决方案.对于移动Web开发来说,兼容是开发重心,通常要花费30%甚至更多的时间去处理一些兼容问题,甚至时间花掉了,问题依 ...

  9. 移动Web应用开发入门指南——视觉篇

    视觉篇 智能移动设备由于发展历史短,但更新速度快,从而导致移动设备的物理属性差异巨大,其中一部分物理属性影响视觉,另一部分影响到交互.兼容或性能.对人类来说,至少有80%以上的外界信息通过视觉获得,视 ...

随机推荐

  1. 使用模板类导致error LNK2019: 无法解析的外部符号

    原地址 1.定义模板类: template<class T> class Stack {....}; 2.定义模板成员函数: 每个函数头都要以相同的模板声明打头,并将类限定符改成:类名&l ...

  2. Java创建、重命名、删除文件和文件夹(转)

    Java的文件操作太基础,缺乏很多实用工具,比如对目录的操作,支持就非常的差了.如果你经常用Java操作文件或文件夹,你会觉得反复编写这些代码是令人沮丧的问题,而且要大量用到递归. 下面是的一个解决方 ...

  3. wpf dll和exe合并成一个新的exe

    原文:wpf dll和exe合并成一个新的exe 微软有一个工具叫ILMerge可以合并dll exe等,但是对于wpf的应用程序而言这个工具就不好用了.我的这方法也是从国外一个博客上找来的.仅供大家 ...

  4. Hadoop Hive与Hbase关系 整合

    用hbase做数据库,但因为hbase没有类sql查询方式,所以操作和计算数据很不方便,于是整合hive,让hive支撑在hbase数据库层面 的 hql查询.hive也即 做数据仓库 1. 基于Ha ...

  5. 设置SVN忽略文件和文件夹(文件夹)

    在多数项目中你总会有文件和文件夹不须要进行版本号控制.这可能包含一些由编译器生成的文件,*.obj,*.lst,也许是一个用于存放可运行程序的输出文件夹.仅仅要你提交改动,TortoiseSVN 就会 ...

  6. hdu2639(背包求第k优解)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2639 题意:给出一行价值,一行体积,让你在v体积的范围内找出第k大的值 分析:dp[i][j][k]表 ...

  7. BGP的状态机制

    Idle 状态:即空闲状态,不接受任何BGP的连接,等待Start事件的产生,如果有start事件产生,若有start事件产生,系统开启ConnectRetry定时器,向邻居发起TCP连接,并将状态变 ...

  8. 《Linux命令行与shell脚本编程大全》 第十六章 学习笔记

    第十六章:创建函数 基本的脚本函数 创建函数 1.用function关键字,后面跟函数名 function name { commands } 2.函数名后面跟空圆括号,标明正在定义一个函数 name ...

  9. java单例模式(线程安全,效率高,双重推断)

    这样的方法,在获取单利的时候,避免了线程锁,导致訪问该方法速度非常慢, 同是,防止了多线程同事房屋该方法就会产生多个实例的问题. 效率高.线程安全. public class TestInstance ...

  10. OpenCL 查看设备信息

    好久没搞OpenCL了.可是这是个好东西.不能不学,之前发了篇设置OpenCL的文章.看的人还真多,看来大家都知道这个好东西了,都想把OpenCL搞起.只是学习难度还是相当高的. 之前忙搞算法,所以非 ...