來自:http://www.cnblogs.com/xiaozhi_5638/p/4019065.html
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,譯者注)。