(本文翻譯自CodeProject上的一篇關於ASP.NET MVC的文章,原文地址:http://www.codeproject.com/Articles/821275/Webforms-vs-MVC-and-Why-MVC-is-better。注意文章有些地方出現的”MVC“術語指”ASP.NET MVC“,比如本文標題。本文有助於ASP.NET新手學習基礎概念。)
主要內容
- 前提
- 介紹
- ASP.NET Webforms Behind Code的好處和存在的問題
- 問題1:使用“基於檢視”的解決方案去應對“基於行為”的需求
- 問題2:壞的架構模式帶來的副作用:緊耦合
- 問題3:HTML並不是伺服器返回資料的唯一格式
- 問題4:“檢視”與“資料”的靈活組合(這裡其實是指MVC的優點,譯者注)
- 問題5:將Behind Code程式碼定義成一個普通類有益於單元測試(這裡其實是指MVC的優點,譯者注)
- MVC是解決以上問題的有效方案?
來自一個父親的請求:
謝謝我女兒Sanjana繪製的上面那張圖,我很高興如果你們能夠分享臉譜上的這張圖片(鑑於我朝不讓簡單開啟,網址略,譯者注),因為這會激勵她努力成為一個漫畫家。我僅僅想讓她知道她在繪畫這方面多麼優秀,所以希望她可以考慮把繪畫當做未來的職業目標。
順便說一下,上面那張圖會讓你更直觀地知道為什麼ASP.NET MVC比Webforms好。開頭看懂這張圖可以幫助你之後將很多東西聯絡在一起。
前提
這篇文章我將會提到兩個比較常見的術語:ASP.NET Webforms和ASP.NET MVC,很多開發者認為ASP.NET不同於MVC,事實上,MVC是一種架構模式,ASP.NET是一個開發框架。如果你是那群認為ASP.NET不同於MVC的人,那麼我建議你先閱讀這篇文章http://computerauthor.blogspot.in/2014/08/aspnet-vs-mvc-vocabulary-confusion_15.html。
介紹
如果你看了最近微軟公司的議事日程,你會發現他們一直把焦點放在了MVC之上。那麼為什麼微軟熱衷於去推翻一個本來已經很成功的產品(如ASP.NET Webforms)而極力建議開發社群的開發者們轉向使用ASP.NET MVC呢?
這就是本篇文章著重要講到的內容。
ASP.NET Webforms Behind Code的好處和存在的問題
ASP.NET Webforms是一個RAD/VISUAL(快速視覺化)的Web程式開發技術。也就是說,開發者簡單地拖拽控制元件到窗體設計器上,VS就會在Behind Code(aspx.cs檔案,譯者注)生成程式碼。
換句話說,你向設計器中拖放一個Button按鈕後,便可以在它的事件處理程式中編寫程式碼了。
Behind Code檔案就是開發者能夠快速開發Webforms程式的關鍵,因為它封裝了底層複雜的技術過程,如event、delegates、HTTP協議Post、Get以及Session管理等等。你可以閱讀這篇部落格Why Microsoft has partial classes(雖然本文反對者居多,但是我覺得還是有一些道理,譯者注),瞭解微軟在UI設計方面取得成功的故事。
但正是Behind Code的工作方式給開發Web程式帶來了5個嚴重問題,下面我們來討論一下這5個問題以及MVC是怎樣解決這些問題的。
問題1:使用“基於檢視”的解決方案去應對“基於行為”的需求
Web網站最終是給終端使用者使用的,終端使用者帶著特定的目的去訪問一個網站,然後他們使用一些“行為動作”(比如輸入URL、點選提交按鈕等等,譯者注)來表達他們想要幹什麼。比如一個人去購物網站購物,那麼他會通過以下行為來表達他想幹什麼:
- 買東西
- 列印發票
以上這些行為就會通過類似點選按鈕、右鍵或者在瀏覽器位址列中輸入URL來完成。正是因為以上這些行為的構成特點,所以Web程式選擇使用HTTP協議,因為該協議包含了許多與之相似的動作諸如POST、GET、PUT以及DELETE等等,這些恰恰能更形象地表達終端使用者的意圖。這樣很自然地,我們要是能夠把使用者的這些行為一一對映到我們程式方法(函式)上,這不僅會更有意義還會使專案架構更加清晰明瞭。
但是,微軟無法這樣去做。因為微軟一直想推廣它的“快速應用程式開發”(RAD)的概念(或者我們也可以稱之為“視覺化程式設計”),所以它最終選擇了一個“基於檢視”的解決方案去應對“基於行為”的需求。
如上圖所示,使用者的“請求過程”呈現出一個古怪的路線(見上圖)。
- 終端使用者通過POST/GET方式傳送一個Request請求
- IIS伺服器將該請求轉到對應的檢視(Page頁面,譯者注)
- 檢視初始化一個頁面,開始頁面生命週期,激發對應事件(如Page.Load,譯者注),最終處理終端使用者的行為(三層架構中,就是呼叫業務邏輯成、資料訪問層進行處理,譯者注)
- 最終伺服器將結果以HTML的形式Response給終端使用者的瀏覽器
如上,微軟搞出了一個“基於檢視”的架構方案去應付一個“基於行為”的需求。換句話說,如果一個終端使用者發出了一個“購買”的請求,那麼該請求先被一個類似“Shopping.aspx”的頁面進行處理,然後該頁面再去通知類似“Shopping.aspx.cs”,接著開始一個複雜的頁面生命週期,最後激發對應事件(Page.Load,Button.Click)進行請求處理然後將結果返回給終端使用者。
上面這個過程相當複雜繁瑣,終端使用者的任何一個請求都是需要先經過一個複雜的頁面生命週期之後才能真正被處理。那麼,我們建立一個“面向行為”的架構方案去取代“面向檢視”怎麼樣?
如果我們先處理請求,然後再呈現檢視給終端使用者,這個流程是不是要更清楚明瞭一些呢?事實上,MVC就是這樣做的,使用者請求先被對應的Controller處理,然後再由後者呈現對應的View(附帶Model)。
問題2:壞的架構模式帶來的副作用:緊耦合
一旦你選擇了一個下三濫的架構模式,你後期會為了適應它而不斷地做出妥協,最終出現越來越多的負面效果。ASP.NET Webforms恰恰就是這樣的。Behind Code(aspx.cs檔案,譯者注)從來都不會真正地符合“鬆耦合”的規則,比如ASPX.CS檔案永遠不能與ASPX檔案分離開來。
換句話說,我們不能輕易地將“Customer.aspx.cs”和“CustomerDetailed.aspx”組合到一起,Behind Code和檢視僅僅關聯在一起,不能被複用。
如果你比較過Behind Code程式碼和專案中其它模組程式碼,你會發現前者不但體積龐大而且還充斥著不計其數的事件處理程式程式碼。這不僅使程式碼不易閱讀,後期維護更是難上加難。
如果我們將“檢視優先”的架構方案換成“行為優先”的方案,我們就很容易地重用一部分邏輯程式碼,並且呈現給終端使用者的檢視可以隨意切換。比如,如果一個終端使用者傳送一個“Display”的請求,那麼我們可以選擇將“DisplayDesktop.aspx”或者“DisplayMobile.aspx”傳送給終端使用者,而這完全取決於使用者當前使用裝置。
在MVC中,我們可以很輕鬆決定到底顯示“MobileView”還是“NormalView”,你可以想象,在Webforms中,實現這個是多麼複雜。
問題3:HTML並不是伺服器返回資料的唯一格式
在Webforms中,檢視和Behind Code不僅處於一種“緊耦合”狀態,就連伺服器返回的資料格式也是相當固定的,預設為HTML。如果你想要改變返回資料的格式,那麼你得和Content-Type以及Response.End方法打交道了,這是一件多麼頭疼的事情。
事實上,如果我們使用“行為優先”的方案,在處理完使用者請求後,就有很大機會去決定到底給使用者返回什麼格式的資料。下面是一段MVC根據傳進來的引數來決定到底返回JSON還是HTML給使用者的程式碼。這種靈活性在Webforms中幾乎很難實現。
問題4:“檢視”與“資料”的靈活組合(這裡其實是指MVC的優點,譯者注)
當我們給使用者一個Response時,其實包含View和Data兩部分(View代表頁面結構,Data代表頁面資料,譯者注)。Webforms是一個“檢視優先”的架構模式,所以它很難靈活地切換最終呈現給使用者的檢視,不斷如此,檢視還要負責呼叫邏輯處理的程式碼,這完全違背了單一職責原則(SRP)(詳細的SOLID五大設計原則請參見博主前面部落格,譯者注)。
如果我們使用“行為優先”的架構模式,那麼當請求到達時,先經過處理,再才決定呈現給使用者什麼檢視和資料。
MVC中,在處理請求時,你可以編寫如下程式碼。你可以將同一個Model(資料,譯者注)與不同的View進行組合。如下面程式碼中所示,你可以將一個Model(customerdata)與一個View(DetailCustomer)組合,也可以將它與另一個View(Customer)組合。
這種靈活性在Webforms中是非常難以實現的,因為在Webforms中,請求先到達檢視(Page頁面,譯者注),然後由它決定呼叫什麼處理邏輯。檢視在一開始就決定死了。
問題5:將Behind Code程式碼定義成一個普通類有利於單元測試(這裡其實是指MVC的優點,譯者注)
在Webforms中,Behind Code程式碼以一個Partial類的形式出現,它相當複雜(繼承自Page類),而且不能輕易地建立它的例項。預設情況下,每個頁面均繼承自Page類,由於Page類依賴項比較多,所以例項化一個Web頁面物件相當困難(這裡指單元測試的時候,譯者注)。
現在你可能會問,為什麼你要自己例項化一個Page類物件?原因很簡單,因為我要進行單元測試,我要測試按鈕Button1的Click事件處理程式(Button1_Click)是否按照我的預期那樣去執行。
但是問題來了,如果你按照以下方式去編寫測試程式碼,它會丟擲異常
這使得UI這塊的單元測試非常困難:
在MVC中,Behind Code變成了簡單正常的類(Controller中的各種Class,譯者注),建立這些類例項沒有之前那麼費勁。
MVC真是解決以上問題的有效方案?
將“基於檢視”的架構轉變為“基於行為”的架構,我們需要做以下幾個修改(見上圖):
- 將原來所有的Behind Code(aspx.cs檔案)中的程式碼定義成MVC中Controller中的類,並將原來的事件處理程式改成一系列常規方法(我們可以稱之為Action)
- 原來三層架構中的“中間層(BLL)”變成了現在的Model,它負責提供資料以及一些邏輯處理
- View僅僅負責顯示,比如頁面HTML元素的位置、佈局等
- 原來三層架構中的資料訪問層(DAL)不需要做太多的改變,因為原本Behind Code就很少與它打交道
那麼,使用MVC架構後,
- 終端使用者傳送它的請求到Web伺服器,伺服器將其路由給指定的Controller
- Controller找到一個對應的Action進行處理
- 現在,Action有兩件事要做,第一根據需要訪問Model獲取資料,然後再將獲取的資料傳遞給合適的View,最終將View傳送給終端使用者的瀏覽器
ASP.NET Webforms最大的優勢就是RAD和VISUAL(快速視覺化開發),即使現在看來它是那樣的繁瑣和不堪入目,但它確實能夠讓你的程式開發速度加快,準時完工(不考慮其他後果,譯者注)。
在2000年,微軟推出ASP.NET Webforms是一個正確的決定,因為那時候它想吸引那些已經熟悉VB6、VF、VC++等快速開發技術的開發人員,我認為Webforms已經達到了它原來的目的。現在我們是時候邁開腳步去學習更好的架構模式了,比如MVC(對應的ASP.NET MVC,譯者注)。
---------------------------
以上為翻譯全文,斜體字為譯者自行增加的內容。全文並沒有一字一句的按照原文翻譯,部分屬於譯者自己理解而寫出來的。能力有限,翻譯不當之處望海涵。