快捷搜索:

基于.NET的Web应用框架构建模式

本文对应于Web表示模式集群,文章的前半部分重文字的描述了MVC模式的架构、设计及其ASP.NET实现,而在加倍繁杂的系统中,随后提出了Page Controller(页面节制器)和Front Controller(前端节制器)作为MVC实现的弥补,着末,简要先容了Web表示模式集群的别的两个模式:Intercepting Filter(筛选器)和Page Cache(页面缓存)模式。

“体系布局设计者的第一个作品每每对照简练和干净。他知道自己并不懂得正在进行的事情,是以他胆小如鼠地设计它。在他设计第一个作品时,会进行多次修饰和润饰。这些会留到“下一次”应用……这第二个系统是他曾经设计的最危险的系统……一样平常趋势是,在设计第二个系统时,将会应用在第一个作品中被小心弃置在一边的所有思路和修饰,从而导致设计过了头。”

—Frederick P. Brooks, Jr.颁发于1972年的The Mythical Man Month(人月神话)。

Web上建立的第一个系统是简单地链接在一路的静态HTML页面,以便在分散的小组之间共享文档。跟着用户的应用量增添,可响利用户输入的动态网页日益普遍。早期的动态页面一样平常因此通用网关接口(CGI)脚本的形式编写的。这些CGI脚本不仅包孕用来确定在响利用户输入时该当显示什么内容的营业逻辑,而且还会天生表示HTML。跟着对更繁杂逻辑需求的增添,对更富厚、更活跃的表示形式的需求也随之增添。这种增添了的繁杂性给CGI编程模型带来了寻衅。

不久,基于页面的开拓手段(如ASP和JSP)呈现了。这些新措施容许开拓职员将脚本直接嵌入到HTML面中,从而简化了编程模型。当这些嵌入的脚本利用法度榜样变得更繁杂时,开拓职员盼望在页面级别将营业逻辑与表示逻辑分开。为适应这一要求,随之呈现了具有赞助器工具和代码暗藏页面策略的标记库。然后,又呈现了供给动态设置设置设备摆设摆设站点导航和敕令调整法度榜样的精细框架,但所有这统统都因此增添繁杂性为价值的。假设现在有大年夜量的Web表示可选规划,若作甚您的利用法度榜样选择适当的Web表示设计计算?

是否真的有一个设计计算能够适应所有的环境?很不幸,在软件设计中,打消过多的冗余和过度的繁杂性是一个竞争性需求,很难能够真正做到两者之间的平衡。您可以从包孕嵌入脚本的简单页面开始设计事情,但很快营业逻辑就会赓续重复呈现在各个文件中,从而导致系统难以掩护和扩展。经由过程将该逻辑移到一组协作组件中,可以打消冗余,然则这样做会增添办理规划的繁杂性。另一方面,您的设计事情可以从设计用来供给标记库、动态设置设置设备摆设摆设和敕令调整法度榜样的框架入手,可是这样虽然能够打消冗余代码,但它会大年夜大年夜增添系统的繁杂性,而这平日是不需要的。

而若何斟酌各个方面的需求,提出一个最相宜我们利用的Web表示策略呢?这必要在繁杂办理规划(支持将来可能发生变更的情形)和简单办理规划(满意今朝的要求)之间做出决定,原则上适当增添资源是可取的,而过多增添资源却是弗成取的。那么废话少说,我们就从“简单”开始吧。

MVC(模型—视图—节制)

许多谋略机系统的用途都是从数据存储检索数据并将其显示给用户。在用户变动数据之后,系统再将更新内容存储到数据存储中。由于关键的信息流发生在数据存储和用户界面之间,以是您可能倾向于将这两部分绑在一路,以削减编码量并前进利用法度榜样机能。然则,这种看起来自然而然的措施有一些大年夜问题。一个问题是,用户界面的变动每每比数据存储系统的变动频繁得多。将数据和用户界面这两部分耦合在一路带来的另一个问题是,营业利用法度榜样每每会并入远不止数据传输功能的其他营业逻辑。若何让Web利用法度榜样的用户界面功能实现模块化,以便您可以轻松地零丁改动各个部分?

Model-View-Controller恰是这样的模式,它实现功能模块和显示模块的分离,使得利用法度榜样加倍可掩护,可扩展,可移植和可复用,它最初是Trygve Reenskaug在二十世纪七十年代末为Smalltalk平台开拓的框架[Fowler03],而成长到今朝为止,已经形成了一个异常成熟的模式。

MVC办理规划

Model-View-Controller (MVC)模式基于用户输入,将域的建模、显示和操作分为三个自力的类[Burbeck92]:

模型。模型用于治理利用法度榜样域的行径和数据,并相应为获取其状态信息(平日来自视图)而发出的哀求,还会相应变动状态的指令(平日来自节制器)。

视图。视图用于治理信息的显示。

节制器。节制器用于解释用户的鼠标和键盘输入,以看护模型和/或视图进行响应的变动。

图1、描述了这三个工具之间的布局关系

视图和节制器都依附于模型。然则,模型既不依附于视图,也不依附于节制器。这是分离的主要优点之一。这样的分离容许模型在自力于可视表示功能的环境下建立和测试。在许多胖客户端利用法度榜样中,视图与节制器的分离是次要的,实际上,许多用户界面框架将角色实现为一个工具。另一方面,在Web利用法度榜样中,视图(浏览器)与节制器(处置惩罚HTTP哀求的办事器端组件)的分离是很好定义的。

Model-View-Controller是一个用于将用户界面逻辑与营业逻辑分分开来的根基设计模式。遗憾的是,此模式的遍及导致了许多差错的描述。分外是在不合的高低文中,术语“节制器”已经用于意指不合的事物。幸运的是,Web利用法度榜样的呈现已经赞助打消了一些不明确性,由于视图与节制器的分离是如斯显着。

MVC的变型

在Application Programming in Smalltalk-80: How to use Model-View-Controller (MVC) [Burbeck92]中,Steve Burbeck描述了MVC的两个变型:被动模型和主动模型。

当一个节制器以独有要领操作模型时,将应用被动模型。节制器将改动模型,然后看护视图:模型已经变动,应该进行刷新(见图2)。此环境下的模型完全自力于视图和节制器,这意味着模型无法申报其状态变动。HTTP协议是此规划的示例。浏览器没有从办事器获取异步更新的简单措施。浏览器显示视图并对用户输入作出相应,然则它不会检测办事器上的数据变动。仅当用户显式哀求刷新时,才会扣问办事器是否发生了变动。

图2、被动模型的行径

当模型变动状态而不涉及节制器时,将应用主动模型。当其他资本正在变动数据并且变动必须反应在视图中时,可能会发生这种环境。以股票报价机的显示为例。您从外部源接管股票数据,并盼望当股票数据变动时更新视图(例如,报价机数据区和警告窗口)。由于只有模型检测对其内部状态的变动(在这些变动发生时),以是模型必须看护视图刷新显示。

然则,应用MVC模式的一个目的是使模型自力于视图。假如模型必须将变动看护视图,则会从新带来您盼望避免的依附性。幸运的是,Observer模式[Gamma95]供给了这样的机制:提醒其他工具留意状态的变动,而不会导致对这些工具的依附性。各个视图实现Observer接口,并向模型注册。模型将跟踪由订阅变动的所有察看器组成的列表。当模型发生改变时,模型将会遍历所有已注册的察看器,并将变动看护它们。此措施平日称为“宣布-订阅”。模型从不必要有关任何视图的特定信息。实际上,在必要将模型变动看护节制器的环境下(例如,启用或禁用菜单选项),节制器必须做的整个事情是实现Observer接口并订阅模型变动。对付存在许多视图的环境,定义多个主体是故意义的,此中每个主体都描述了特定类型的模型变动。然后,每个视图都只能订阅与视图有关的变动类型。图3显示了应用Observer的主动MVC的布局,以及察看器若何将模型与直接引用视图隔脱离来。

图3、在主动模型中应用察看器将模型与视图分离

图4阐明当模型发生改变时Observer若何看护视图。可惜的是,在统一建模说话(UML)序列图中,没有好的措施来演示模型与视图的分离,由于该图表示的是工具的实例而不是类和接口。

图4、主动模型的行径

MVC的ASP.NET实现

为相识释若何在ASP.NET中实现Model-View-Controller模式,并阐明在软件平分离模型、视图和节制器角色的好处,下面的示例将一个没有分离所有三个角色的单页面办理规划重构为分离这三个角色的办理规划。示例利用法度榜样是一个带有下拉列表的网页(如栏下图所示),该页面显示了存储在数据库中的记录。

使用Microsoft Visual Studio(r) .NET开拓系统的代码暗藏功能,可以很轻易地将表示(视图)代码与Model-Controller代码分分开来。每个ASP.NET页都有一种机制,这种机制容许在零丁的类中实现从网页调用的措施。该机制是经由过程Visual Studio .NET供给的,它有许多优点,例如Microsoft IntelliSense(r)技巧。当您应用代码暗藏功能来实现网页时,可以应用IntelliSense来显示网页后面的代码中所应用的工具的可用措施列表。IntelliSense不适用于.aspx页。与此同时,为了展现Model-Controller的分离,对付数据库操作提取了DatabaseGateway,这样就实现了三者的完备分离。

视图

录音

选择录音:

模型

using System;

using System.Collections;

using System.Data;

using System.Data.SqlClient;

public class DatabaseGateway

{

public static DataSet GetRecordings()

{

String selectCmd = "select * from Recording";

SqlConnection myConnection =

new SqlConnection(

"server=(local);database=recordings;Trusted_Connection=yes");

SqlDataAdapter myCommand = new SqlDataAdapter(selectCmd, myConnection);

DataSet ds = new DataSet();

myCommand.Fill(ds, "Recording");

return ds;

}

public static DataSet GetTracks(string recordingId)

{

String selectCmd =

String.Format(

"select * from Track where recordingId = {0} order by id",

recordingId);

SqlConnection myConnection =

new SqlConnection(

"server=(local);database=recordings;Trusted_Connection=yes");

SqlDataAdapter myCommand = new SqlDataAdapter(selectCmd, myConnection);

DataSet ds = new DataSet();

myCommand.Fill(ds, "Track");

return ds;

}

您可能还会对下面的文章感兴趣: