什么是代码结构的组织?
asp.net MVC 5 默认创建出的几个目录的标准含义分别如下:

  • Controllers目录存放MVC模式中的Controler
  • Models目录存放MVC模式中的Model
  • Views目录存放MVC模式中的View

除此外还有Content(存放资源文件,如CSS、图片)、Scripts(JS脚本文件)等目录是有标准含义的,有标准含义也就是说有非标准含义,你可以遵循这些标准含义也可以定义自己的非标准含义,比如:

  • asp.net MVC 5 中的Controller无非是继承system.web.mvc.controller的类而已,你可以把这个class放在其他目录甚至另外一个Project中。
  • asp.net MVC 5 的Model,常见做法是将它独立在一个Class Lib类型的Project中
  • asp.net MVC 5 的View默认为Razor引擎,在默认的规则中用Views目录存放所有的视图,你可以通过修改规则来将视图放在其他目录中,或者你用其他的视图引擎甚至自定义视图引擎

这就是我所谓的代码结构的组织,本质上其实就是MVC模式(在看MVC各种理论的时候注意一个心态:MVC模式本身也是发展变化的,每个具体的MVC模式的实现(比如asp.net mvc)或多或少都在理论的基础上做了修改,和理论不能完全吻合上很正常,我在学习之初太过死板,在这方面纠结了很久时间)在asp.net MVC中的实现,当然远没有上面所述的仅仅是几个目录的分类这么简单,比如Model,如果仅仅是把它独立在一个Class Lib中的话,仔细想想,我们以前不都是这么做的吗?

有个PHP平台的框架YII所遵循的一些MVC实践原则我觉得是总结的比较实用的(以下内容是这些实践内容的核心,部分为我自己对原文的理解加变化):

模型(Model)用于表示底层数据结构,经常在整个应用的不同部分共享,有些模型在前后台、API中都会用到,所以一个Model应该遵循的指导原则有:

  • 包含属性用于描述特定的数据
  • 应该包含业务逻辑,以确保数据能够满足表现的需要
  • 应该包含数据操作的代码,比如数据存储、检索
  • 确保在不同的环境中均可实用,比如B/S环境、控制台应用或者作为单纯的API
  • 不应该出现HTML代码,负责表现的代码应该放到view文件中(这点是WebForm时代为了实现动态响应而经常这么处理的,而现在我们有Javascript框架可以选择,如JQuery)

在上述指导原则下,可能会写出非常庞大的Model类(过多数据操作,业务逻辑代码包含其中)。这种情况下,建议进一步抽象,提炼出一个基类,包含最通用的功能,然后前端、后端和API在用到时候,将各个子应用才相关的逻辑放到基类继承出来的子类里面。

视图(View)主要就用于前端表现的代码:

  • 包含HTML,以及所有负责表现的代码,可以出现.net语法,但是只用于遍历数据、格式化数据(在asp.net mvc + JQuery环境中,用JQuery语法来遍历、格式化、构造Html呈现数据而不用.net语法)
  • 不应该包含DB请求
  • 仅关于表现,布局等和页面呈现有关的业务出现在View中,用户的请求数据应该由Controller和Model负责处理
  • 如果必要,可以访问Model和Controller的属性,不过这是为了满足表现的需要(asp.net mvc 5 默认创建的view就是直接访问Model的属性来呈现数据的,老实说这让我很Confuse<困惑、糊涂>:MVC模式本身不是为了解耦的吗?且很多说法都是视图都是通过Controller来与Model关联而不是直接与Model关联。在asp.net mvc + JQuery环境中,所有视图通过JQuery访问Controller来获得返回数据以呈现,也通过JQuery访问Controller来修改数据)

可以使用诸如布局、部分视图等框架特性来最大程度重用View的代码。

控制器(Controller)是将模型、视图和其他组件组装在一起形成一个应用的粘合剂。控制器直接负责处理终端用户的请求。

  • 处理终端用户发出的GET或POST请求,GET用来获取数据,POST用于修改数据
  • 创建模型,并决定一个模型对象的生命周期
  • 不应该出现SQL语句,数据库请求应该放到Model中(在WebForm的实践做法中,我们创建了一堆的API、方法、函数,很多也没有出现SQL语句,在考虑重构时这很让我抓狂:什么样的代码应该放在Controller中,什么样的代码应该放在Model中,即便有了这个原则之后也是)
  • 不应该出现HTML代码,而应该将其放入到View中

在一个设计良好的MVC应用中,控制器是非常轻量级的,经常只有几十行代码的样子;而Model总是非常复杂而且庞大,包含了所有的用于表现的数据及其操作方法。这是因为由数据结构和业务逻辑组成的模型对每个应用来说,都是独特的,需要大量的定制化工作来满足应用的需求;控制器的逻辑经常遵循一个特定的套路,在各个应用中都差不多,因此可以被框架底层代码极大程度地简化(也就是说不是控制器代码少,而是Web开发框架已经都抽象出来并且都帮你做好了,这也 就是框架的价值和能够实现快速开发的原因)。

遵循以上代码结构组织实践,可以有一个基于asp.net mvc 5构建的Web Application,这个project主要处理Controller和View,关于Controller的实践原则以上四条是远远不够的,Controller的作用是通过调度来达到控制输出(视图或数据)的目的(当我们说这句话时,实际上指的是Controller的Action的实践原则。另注意:这里提及的调度和控制两个词不是技术术语,就是通俗语言的说法而已),举例来说:

  • MVCStore示例将身份验证作为一个AuthenticationController,此控制器内不同的方法(Action)可实现不同的身份登录(想象一下:AD登录、Form登录,这是企业内部应用常见的两种身份验证方式,我最近参与的一个对公网的系统还涉及到与微信登录的集成),代振军有篇博文对MVCStore的这种做法做了不错的分析,由此我总结的一个Controller开发原则(出发目的是用来应对让我抓狂的"到底哪些代码放入Controller,哪些代码放入Model"的困惑):业务流程放入Controller中,而业务流程中的具体业务规则放入Model中(这是符合SOA构的思想的---好大的帽子吧,可以阅读上面提到的代振军的那篇博文)。基于这个原则,不管是哪一个登录方法,其调度控制的业务流程都应该是这样:
    1. 接收客户端发出的身份信息(通过调度实现):Oxite示例的做法是通过调度,而MVCStore是直接在Action中Request.Form获取,缺点在于会让Action中的代码增长过快
    2. 判断此身份是否合法(通过调度实现):MVCStore中体现的很明显,在Model中创建有对应AuthenticationController的AuthenticationService,通过对此Service的调用来完成此业务流程步骤,而具体的业务规则就放在了Model中(根据此原则可将Model划分为两部分:一是数据模型实体部分,二是业务逻辑部分,如下图所示)
    3. 如果身份合法则认可(这是控制):"如果合法"是业务流程,而"认可"的具体内容(主要指的是对登录身份的保持)则是业务规则,认可后当然就是控制输出视图了(这部分代码我也把它并入到业务流程中)
    4. 如果不合法则不认可(这也是控制):"如果不合法"是业务流程,而"不认可"一般不包含什么业务规则,直接开始控制视图输出

对于Controller在实际项目中需要小心时刻留心要始终保持Controller与View的轻快,小心随着业务复杂度提高而产生的困惑:有很多代码你不知道把它放哪,就只好把它放到控制层,最后发现在控制层中塞了太多的代码。有个叫Irwin的人提出了MOVE模式(这里有个简单的翻译版本)作为MVC模式的改进,其出发点也基于此,在这个MOVE模式中MOV从概念定义可以判断出就是对应了MVC(Operations对应着Controller),增加的一个Events似乎是用来使视图和模型直接关联,我猜想作者可能是在开发Controller时很多的Action需要根据复杂业务逻辑对视图产生影响,因而写了很多此类的代码在Controller中,小心的对Controller和View进行结构化组织,就可在很大程度上避免这种情况。

接下来针对我手上的系统的重构做些实际的思考来应用以上这些原则。

Microsoft 2013 新技术学习笔记 三的更多相关文章

  1. Microsoft 2013 新技术学习笔记 一

    有几年没有关注技术了,最近有点时间想把技术重新捡起来,借着重构手上的一个后台管理框架的机会将微软新的几种技术全部应用一下,从目的上来讲并没有希望能对涉及的技术有很深入的了解,所以这个系列的文章(篇幅不 ...

  2. Microsoft 2013 新技术学习笔记 二

    在探讨系统重构的代码结构组织之前,先初步考虑框架与数据库的交互,在.net平台上数据访问方案有人总结为三类:DataSet.ADO.net 2.0.ORM组件.我只熟悉ADO.NET方式,众多的企业特 ...

  3. Microsoft 2013 新技术学习笔记 四

    在继续学习Model的实践经验之前,先思考一下Controller和View的实践原则在本次系统重构中的应用,我手上是一个后台管理系统(不是门户系统.不是具体业务系统),通俗点讲就是给企业的运维人员用 ...

  4. VSTO学习笔记(三) 开发Office 2010 64位COM加载项

    原文:VSTO学习笔记(三) 开发Office 2010 64位COM加载项 一.加载项简介 Office提供了多种用于扩展Office应用程序功能的模式,常见的有: 1.Office 自动化程序(A ...

  5. 物联网学习笔记三:物联网网关协议比较:MQTT 和 Modbus

    物联网学习笔记三:物联网网关协议比较:MQTT 和 Modbus 物联网 (IoT) 不只是新技术,还是与旧技术的集成,其关键在于通信.可用的通信方法各不相同,但是,各种不同的协议在将海量“事物”连接 ...

  6. Oracle学习笔记三 SQL命令

    SQL简介 SQL 支持下列类别的命令: 1.数据定义语言(DDL) 2.数据操纵语言(DML) 3.事务控制语言(TCL) 4.数据控制语言(DCL)  

  7. [Firefly引擎][学习笔记三][已完结]所需模块封装

    原地址:http://www.9miao.com/question-15-54671.html 学习笔记一传送门学习笔记二传送门 学习笔记三导读:        笔记三主要就是各个模块的封装了,这里贴 ...

  8. JSP学习笔记(三):简单的Tomcat Web服务器

    注意:每次对Tomcat配置文件进行修改后,必须重启Tomcat 在E盘的DATA文件夹中创建TomcatDemo文件夹,并将Tomcat安装路径下的webapps/ROOT中的WEB-INF文件夹复 ...

  9. java之jvm学习笔记三(Class文件检验器)

    java之jvm学习笔记三(Class文件检验器) 前面的学习我们知道了class文件被类装载器所装载,但是在装载class文件之前或之后,class文件实际上还需要被校验,这就是今天的学习主题,cl ...

随机推荐

  1. 用 NuGet Package Explorer 管理你的攻城武器

    缘由:每次新建一个工程,总是要从自己的“弹药库”或者之前的工程里面手动引用一些类库和脚本插件,难免有些繁琐和遗漏.想起经常用到的NuGet,跑到NuGet主页一看,发现有 NuGet Package ...

  2. 在Android中进行单元测试遇到的问题

    问题1.Cannot connect to VM  socket closed 在使用JUnit进行测试的时候,遇到这个问题.网上的解释是:使用Eclipse对Java代码进行调试,无论是远程JVM还 ...

  3. [C#] 编程控制笔记本蓝牙与外部蓝牙设备通信

    一.蓝牙模块XLBT232‐D01介绍(外部设备蓝牙) 1.1.蓝牙模块简介 XLBT232-D0101蓝牙模块采用CSR BlueCore 芯片,配置6-8Mbit 的软件存储空间, 支持AT 指令 ...

  4. 为什么要设置getter和setter?

    面向对象语言中,通常把属性设置为私有,然后添加getter和setter方法来访问.有人说,这本质上和设置属性为公有没有区别,干脆把属性public算了.也有人反驳,这样做破坏了封装.但是,破坏了封装 ...

  5. atitit查询表修改表字段没反应--解锁锁定的表

    atitit查询表修改表字段没反应--解锁锁定的表 查询表修改表字段没反应 要是使用gui 没反应,最好使用cmd 方式,不卉不个gui 锁上.. ALTER TABLE t_mb_awardweix ...

  6. Leetcode 7 Reverse Integer 数论

    题意:将整数倒置,该题简单但是需要注意数据的范围,难得的好题. 如果出现1000000003或者-2000000003,倒置后的数超过int的范围,因此返回0,出现这种情况可以使用long long, ...

  7. spring源码 — 一、IoC容器初始化

    IoC容器初始化 注意:本次的spring源码是基于3.1.1.release版本 容器:具有获取Bean功能--这是最基本功能,也是BeanFactory接口定义的主要行为,在添加了对于资源的支持之 ...

  8. photoshop 快速切图

    发现一个详细好方法:http://blog.csdn.net/zhangxiaowei_/article/details/42143307 具体如下:

  9. Java 中的instanceof简单讲解

    Java 中的instanceof 运算符是用来在运行时指出对象是否是特定类的一个实例.instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例. 用法:resu ...

  10. mysql5.5 uuid做主键与int做主键的性能实测

    数据库:mysql5.5 表类型:InnoDB 数据量:100W条 第一种情况: 主键采用uuid 32位. 运行查询语句1:SELECT COUNT(id) FROM test_varchar; 运 ...