1、什麼是設計模式?
模式的理解
有一面牆,現在要讓一組人翻過去。 前面三個人用了三種不同的翻牆方式。後面排隊的人發現第三 個人的方式更好。所以他們都重複第三個人的翻牆方式。 這時,我們就可以說第三個人發現了一種“模式”。 |
模式是一種解決問題的思路,而不是具體的做法。
設計模式的理解
在軟件開發領域,設計模式是爲解決開發中某一類問題而提出的一種解決方案。因此,設計模式本質上是思想,而不是代碼。設計模式追求的思想上的複用,而不是代碼上的複用。
2、ASP.NET MVC概述
ASP.NET MVC是一種構建Web應用程序的框架,它將一般的MVC(Model-View-Controller)模式應用於ASP.NET框架。
MVC將Web應用程序劃分爲三個主要的部分,以下是MSDN給出的定義:
模型(Model):模型對象是實現應用程序數據域邏輯的應用程序部件。 通常,模型對象會檢索模型狀態並將其存儲在數據庫中。 例如,Product 對象可能會從數據庫中檢索信息,操作該信息,然後將更新的信息寫回到 SQL Server 數據庫內的 Products 表中。
視圖(View):視圖是顯示應用程序用戶界面 (UI) 的組件。 通常,此 UI 是用模型數據創建的。 Products 表的編輯視圖便是一個視圖示例,該視圖基於 Product 對象的當前狀態顯示文本框、下拉列表和複選框。
控制器(Controller):控制器是處理用戶交互、使用模型並最終選擇要呈現的視圖來顯示 UI 的組件。 在 MVC 應用程序中,視圖僅顯示信息;控制器則用於處理和響應用戶輸入和交互。 例如,控制器處理查詢字符串值,並將這些值傳遞給模型,而模型可能會使用這些值來查詢數據庫。
3、ASP.NET MVC的發展歷程
我們以一張時間軸線圖開始,瞭解一下ASP.NET MVC的發展歷程。
2007年2月,Microsoft公司的Scott Guthrie在旅途中草擬了ASP.NET MVC的內核程序。經過9個預覽版本,於2009年3月13日,正式發佈ASP.NETMVC1官方版本。
時隔一年,ASP.NETMVC2於2010年3月發佈,部分主要特徵如下:
• 帶有自定義模板的UI輔助程序
• 在客戶端和服務端基於特性的模型驗證
• 強類型的HTML輔助程序
• 改善的Visual Studio開發工具
• 支持將大型應用程序劃分爲域
• 支持異步控制器
• 使用Html.RenderAction支持渲染網頁或網站的某一部分
• 新的輔助函數、使用工具和API增強
2011年1月,ASP.NET MVC3正式推出,部分主要特徵如下:
• 支持Razor視圖引擎
• 支持.NET4數據註解
• 改進了模型驗證
• 提供更強的控制和更大的靈活性,支持依賴項解析(Dependency Resolution)和全局操作過濾器(Global Action Filter)
• 豐富的JavaScript支持,其中包括非侵入式JavaScript、jQuery驗證和JSON綁定
• 支持NuGet,可以用來發布軟件,管理整個平臺的依賴
2012年9月,ASP.NET MVC4正式發佈,新增功能主要包括:
• ASP.NET Web API
• 增強了默認的項目模板
• 添加使用jQuery Mobile的手機項目模板
• 支持顯示模式(Display Mode)
• 支持異步控制器的任務
• 捆綁和微小(minification)
2013年10月,ASP.NET MVC5與Visual Studio 2013一起發佈,下面列出了一些主要特徵:
• One ASP.NET
• 新的Web項目體驗
• ASP.NETIdentity
• Bootstrap模板
• 特性路由
• ASP.NET基架
• 身份驗證過濾器
• 過濾器重寫
4、創建ASP.NET MVC4應用程序
1、選擇“文件”->“新建”->“項目”選項,如下圖所示:
2、在“新建項目”對話框左側的“已安裝”->“模板”->“Visual C#”列表下,選中Web選項,選擇ASP.NET MVC4 Web應用程序,將應用程序命名爲MyFirstMvcProject,點擊“確定”按鈕,如下圖所示:
3、在彈出的“新ASP.NET 項目”對話框中,選中“基本”模板,視圖引擎選擇“Razor”,同時將“創建單元測試項目”勾選上,點擊“確定”按鈕,如下圖所示:
4、至此,一個新的MVC項目已經創建完成,如下圖所示:
5、ASP.NET MVC應用程序結構
新的MVC項目創建完成後,會自動向這個項目中創建一些目錄,下表介紹了這些目錄的主要用途。
目錄 | 用途 |
App_Data | 用於存儲想要讀取/寫入的數據文件 |
App_Start | 用於保存一些功能的配置代碼 |
Content | 用於保存CSS、圖像和其他站點內容 |
Controllers | 用於保存處理URL請求的控制器類 |
Models | 用於保存表示和操縱數據以及業務對象的類 |
Scripts | 用於保存JavaScript庫文件和腳本 |
Views | 用於保存負責呈現輸出結果的UI模板文件 |
6、ASP.NETMVC的約定
在默認情況下,ASP.NET MVC應用程序對約定的依賴性很強,這樣就避免了開發人員配置和指定一些項,因爲這些項可以根據約定來推斷。這個概念通常被稱爲“習慣優於配置(convention over configration)”。
ASP.NET MVC對於程序結構的約定如下:
1、每個Controller類的名字以Controller結尾,保存在Controller目錄中。
2、應用程序的所有視圖放在單獨的Views目錄下。
3、控制器使用的視圖是在Views主目錄下的,與控制器名稱相同的子目錄中。
7、WebForm和ASP.NET MVC,爲什麼MVC更好一些?
ASP.NET Webform 後臺代碼(behind code)—— 福音與詛咒
我們已經在項目開發中使用過ASP.NETWebform技術,大家會發現它更接近可視化設計,換句話說,開發者只需要從設計面板中拖拽控件即可完成UI,接着在behindcode中實現邏輯代碼即可完成最後的Web頁面功能。
所以換句話說,當你從設計面板中拖拽一個按鈕時,在後臺代碼中就會生成一個button對象,你只需要在按鈕的點擊事件中實現事件響應代碼即可。
public partial class WebForm1 :System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// Developers write code here
}
protected void Button1_Click(object sender, EventArgs e)
{
// Developers write code here
}
}
當我們在頁面中拖拽一些UI元素時,雙擊它們即可在後臺代碼中生成一系列事件響應代碼,這些邏輯代碼都在ASPX.CS文件中。
這個後臺代碼文件是ASP.NETWebForm的關鍵,你可以在這個文件中應用.NET的所有特性,包括事件、委託、HTTP協議以及Session等等。
但是這種behindcode模式有5個問題,下面我們將一一講述這5個問題,並用MVC的設計思想來分別解決這些問題。
問題1:基於視圖的方案來解決基於行爲的需求
我們的網站最終是由用戶使用的,用戶訪問網站肯定會有特定的目的,網站要做的就是通過讓用戶的交互行爲來完成其想要的目的。比如當用戶訪問一個購物網站時,也許他的交互行爲會是這樣的:
· 購買產品
· 打印發票
這些交互行爲是通過按鈕點擊、右鍵點擊和瀏覽器URL實現的。由於這些交互都是基於HTTP協議的,所以如果我們能將這些交互行爲映射到具體的一些方法上,那麼整個架構將會變得簡單很多。
但是微軟做不到這樣,因爲它要實現可視化網頁編程,所以他們最終選擇了基於視圖的解決方案。
從上圖可以看出,整個請求過程看上去很奇怪:
· 用戶發起一個HTTP請求,比如HTTPPOST / GET
· IIS服務器將請求映射到視圖
· 視圖調用頁面的生命週期,通過事件驅動,調用合適的交互方法
· 最後將交互的結果展現給終端用戶
因爲微軟一開始就選擇了基於視圖的設計方案,所以架構本身很難向基於用戶交互的設計思想靠攏。換句話說,當用戶發出“購買”請求時,先是訪問了視圖頁面“Shopping.aspx”,後臺邏輯代碼在“Shopping.aspx.cs”中,頁面生命週期中會將頁面的計算結果返回給用戶。
如果利用MVC的思想,都是基於用戶交互行爲的話,那麼請求流程將會是如下所示:
問題2:壞架構的副作用——緊耦合
當選擇了一個錯誤的架構以後,未來將會出現很多難以解決的副作用,在ASP.NETWebForm中就出現了這個問題。儘管behindcode後臺代碼被分離到不同的文件中,但是ASPX.CS文件和ASPX文件卻緊密的聯繫在一起,這將導致系統的耦合度很高,並且很難解耦合,這是一個很頭疼的問題。
簡單地說,我們很難將Customer.aspx.cs和CustomerDetailed.aspx簡單地剝離開,後臺代碼已經緊緊地將其捆在一起,而且也很難複用。如果我們可以將請求先通過action,而不通過視圖view,action得到的數據再由控制器決定由哪個view展示,那麼請求的流程將會是這樣的:
所以我們可以很方便地控制最終結果是由移動頁面展示還是正常頁面展示,如下代碼:
publicActionResult Index(string DeviceType)
{
if (viewType == "Mobile")
{
returnView("MobileView");
}
else
{
returnView("NormalView");
}
}
問題3:HTML不是唯一的返回類型
由於視圖view和後臺代碼behindcode緊密耦合在一起,所以默認的返回類型就固定了,都是HTML類型。如果你想改變類型就必須設置Content-type和調用Response.End方法。
如果我們創建一個Action,返回的類型由Action中指定,系統就可以在同一個action中根據不同條件輸出不同的返回類型。代碼如下:
publicActionResult Index(string viewType)
{
if (viewType == "JSON")
{
return Json(new Customer(),JsonRequestBehavior.AllowGet);
}
else
{
returnView("DisplayCustomer", new Customer());
}
}
問題4:視圖和數據的靈活組合
Webform是視圖優先的架構,所以視圖決定了展現的數據,因此視圖的擴展性就很差,如果遇到複雜的數據結構,這種方式就顯得力不從心了。
但是如果是行爲優先的架構的話,當我們觸發action時,action可以根據不同的請求選擇不同的數據模型和視圖結構,如下圖:
在MVC中,你可以在不同的view中選擇相同的數據模型,比如下面的代碼,customerdata數據既可以綁定在DetailCustomer視圖中,也可以綁定在Customer視圖中。
publicActionResult Index(string ViewName,Customer customerdata)
{
if (ViewName =="Detailed")
{
returnView("DetailCustomer",customerdata);
}
else
{
returnView("Customer",customerdata);
}
}
但這在WebForm中實現起來是非常麻煩的。
問題5、將behind code當做普通的類來進行單元測試
behindcode後臺代碼在WebForm中是一個非常龐大的類,並且不能簡單地實例化。要知道WebForm是繼承於Page類的,Page類不能直接實例化,因爲它有太多的依賴項。
publicpartial class WebForm1 : System.Web.UI.Page
{
protected void Page_Load(object sender,EventArgs e)
{
}
public void Button1_Click(objectsender, EventArgs e)
{
Session["SomeSession"] ="Is this set";
}
}
爲什麼我們想要實例化Page類呢?其中一個原因就是可以方便單元測試。比如我要測試一個按鈕點擊事件,用來檢查Session是否設置成功。在WebForm中的代碼看起來不是那麼舒服:
[TestMethod]
publicvoid TestMethod1()
{
WebApplication22.WebForm1 obj = newWebApplication22.WebForm1();
obj.Button1_Click(this, newEventArgs());
}
並且運行時還會拋出一個異常:
在MVC中,這個類變成了一個普通類,我們可以在測試工程中將它實例化,並對類裏面的屬性方法、Session、ViewBag 、 TempData等進行單元測試。
publicclass HomeController : Controller // this class is simple
{
public ActionResult Index()
{
Session["SomeSession"] ="Is this set";
return View("SomeView");
}
}
所以是否選擇MVC解決方案?
從WebForm架構切換到MVC架構,我們需要做以下幾件事情:
· 將behindcode中的代碼轉移到controller類中,並將原來的方法轉換成action方法。
· 中間層用數據模型和邏輯接口代替。
· 視圖view只用來展現數據和頁面佈局。
· DAL層和其他層沒有什麼變化,因爲它和behindcode關係不大。
所以MVC架構中,用戶的請求分爲下面3個步驟:
· 終端用戶發送請求,路由器將請求路由到合適的Controller,Controller是邏輯實體和行爲Action的集合。
· Controller將請求映射到特定的Action。
· Action有兩個任務,第一是獲取合適的數據,第二是將這些數據和視圖View綁定起來。Action創建數據模型,並將數據模型連接到指定View,輸出最終的相應結果。