原文地址: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. Python heapq 模块的实现 - A Geek's Page

    Python heapq 模块的实现 - A Geek's Page Python heapq 模块的实现

  2. php使用http请求头实现文件下载

    众所周知php对http协议的依赖特别强,像java或者asp.net在某些情况下可以不依赖http例如asp.net的winform,对php来说文件下载可以使用http的请求头加上php的IO就可 ...

  3. poj 2038 Team Rankings 枚举排列

    //poj 2038 //sep9 #include <iostream> #include <algorithm> using namespace std; char s[1 ...

  4. Linux从用户层到内核层系列 - GNU系列之glibc介绍

    题记:本系列文章的目的是抛开书本从源代码和使用的角度分析Linux内核和相关源代码,byhankswang和你一起玩转linux开发 轻松搞定TCP/IP协议栈,原创文章欢迎交流, byhankswa ...

  5. 不错的C++框架: Thrift(2)-传输和网络相关

    不错的C++框架: Thrift(2)-传输和网络相关 - ang639 - 博客频道 - CSDN.NET 不错的C++框架: Thrift(2)-传输和网络相关

  6. linux yum命令

    1 安装yum install 全部安装yum install package1 安装指定的安装包package1yum groupinsall group1 安装程序组group1 2 更新和升级y ...

  7. A Game of Thrones(6) - Catelyn

    Of all the rooms in Winterfell’s Great Keep, Catelyn’s bedchambers(['bedtʃeɪmbə]卧室,寝室) were the hott ...

  8. HDU 4790 Just Random 数学

    链接:pid=4790">http://acm.hdu.edu.cn/showproblem.php?pid=4790 意:从[a.b]中随机找出一个数字x,从[c.d]中随机找出一个 ...

  9. Java集合关于ArrayList

    ArrayList实现源码分析 2016-04-11 17:52 by 淮左, 207 阅读, 0 评论, 收藏, 编辑 本文将以以下几个问题来探讨ArrayList的源码实现1.ArrayList的 ...

  10. MySQL 触发器结构及三个案例demo

    --你必须拥有相当大的权限才能创建触发器(CREATE TRIGGER),如果你已经是Root用户,那么就足够了.这跟SQL的标准有所不同. CREATE TRIGGER语法 CREATE TRIGG ...