![NET平臺上的Model-View-Presenter模式實踐_第1頁](http://file3.renrendoc.com/fileroot_temp3/2022-2/9/d365a2ba-fca9-4791-8f32-15fc4ccb1fea/d365a2ba-fca9-4791-8f32-15fc4ccb1fea1.gif)
![NET平臺上的Model-View-Presenter模式實踐_第2頁](http://file3.renrendoc.com/fileroot_temp3/2022-2/9/d365a2ba-fca9-4791-8f32-15fc4ccb1fea/d365a2ba-fca9-4791-8f32-15fc4ccb1fea2.gif)
![NET平臺上的Model-View-Presenter模式實踐_第3頁](http://file3.renrendoc.com/fileroot_temp3/2022-2/9/d365a2ba-fca9-4791-8f32-15fc4ccb1fea/d365a2ba-fca9-4791-8f32-15fc4ccb1fea3.gif)
![NET平臺上的Model-View-Presenter模式實踐_第4頁](http://file3.renrendoc.com/fileroot_temp3/2022-2/9/d365a2ba-fca9-4791-8f32-15fc4ccb1fea/d365a2ba-fca9-4791-8f32-15fc4ccb1fea4.gif)
![NET平臺上的Model-View-Presenter模式實踐_第5頁](http://file3.renrendoc.com/fileroot_temp3/2022-2/9/d365a2ba-fca9-4791-8f32-15fc4ccb1fea/d365a2ba-fca9-4791-8f32-15fc4ccb1fea5.gif)
版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
1、NET平臺上的Model-View-Presenter模式實踐2010-01-28 21:48 by EricZhang(T2噬菌體), 3889 visits, 網(wǎng)摘, 收藏, 編輯 為什么要寫這篇文章 筆者當(dāng)前正在負責(zé)研究所中一個項目,這個項目基于.NET平臺,初步擬采用C/S部署體系,所以選擇了Windows Forms作為其UI。經(jīng)過幾此迭代,我們發(fā)現(xiàn)了一個問題:雖然業(yè)務(wù)邏輯已經(jīng)封裝到Services層中,但諸多的UI邏輯仍然彌漫在各個事件Listener中,使得UI顯得臃腫不堪,并且存在諸多重復(fù)性代碼。另外,需求提供方說,根據(jù)實際需要,不排除將部署結(jié)構(gòu)改為B/S的可能性,甚至可能會要
2、求此系統(tǒng)同時支持C/S和B/S兩種部署方式。那么,如果保持目前將UI邏輯編碼到Windows Forms中的方式,到時這些UI邏輯將無法復(fù)用,修改部署方式的代價很大。 為了解決以上兩個問題,筆者和相關(guān)人員商量后,決定引入既有成熟模式,重新設(shè)計表示層的架構(gòu)方式,并重構(gòu)既有代碼。 提到表示層(Presentation Layer)的模式,我想大家腦海中第一個閃過的很可能是經(jīng)典的MVC(Model-View-Controller)。我最初也準(zhǔn)備使用MVC,但經(jīng)過分析和實驗后,我發(fā)現(xiàn)MVC并不適合目前的情況,因為MVC的結(jié)構(gòu)相對復(fù)雜,Model和View之間要實現(xiàn)一個Observer模式,并實現(xiàn)雙向通
3、信。這樣重構(gòu)起來Services層也必須修改。我并不想修改Services層,而且我想將View和Model徹底隔離,因為我個人并不喜歡View和Model直接通信的架構(gòu)方式。最終,我選擇了MVP(Model-View-Presenter)模式。 經(jīng)過兩天的重構(gòu)和驗證,目前已經(jīng)將MVP正式引入項目的表示層,并且解決了上文提到的兩個問題。在這期間,積累了少許關(guān)于在.NET平臺上實踐MVP的經(jīng)驗,在這里匯集成此文,和朋友們共享。UI與P Logic 首先,我想先明確一下UI和P Logic的概念。 表示層可以拆分為兩個部分:User Interface(簡稱UI)和Presentation Log
4、ic(簡稱P Logic)。 UI是系統(tǒng)與用戶交互的界面性概念,它的職責(zé)有兩個接受用戶的輸入和向用戶展示輸出。UI應(yīng)該是一個純靜態(tài)的概念,本身不應(yīng)包含任何邏輯,而單純是一個接受輸入和展示輸出的“外殼”。例如,一個不包含邏輯的Windows Form,一張不包含邏輯的頁面,一個不包含邏輯的Flex界面,都屬于UI。 P Logic是表示層應(yīng)有的邏輯性內(nèi)容。例如,某個文本內(nèi)容不能為空,當(dāng)某個事件發(fā)生時獲取界面上哪些內(nèi)容,這都屬于P Logic。應(yīng)該指出,P Logic應(yīng)該是抽象于具體UI的,它的本質(zhì)是邏輯,可以復(fù)用到任何與此邏輯相符的UI。 UI與P Logic之間的聯(lián)系是事件,UI可以根據(jù)用戶的
5、動作觸發(fā)各種事件,P Logic響應(yīng)事件并執(zhí)行相應(yīng)的邏輯。P Logic對UI存在約束作用,P Logic規(guī)定一套UI契約,UI要根據(jù)契約實現(xiàn),才能被相應(yīng)的P Logic調(diào)用。 下圖展示了UI與P Logic的結(jié)構(gòu)及交互原理。圖1、UI與P LogicModel-View-Presenter模式 MVP模式最早由Taligent的Mike Potel在MVP: Model-View-Presenter The Taligent Programming Model for C+ and Java(點擊這里下載)一文中提出。MVP的提出主要是為了解決MVC模式中結(jié)構(gòu)過于復(fù)雜和模型-視圖耦合性過高的
6、問題。MVP的核心思想是將UI分離成View,將P Logic分離成Presenter,而業(yè)務(wù)邏輯和領(lǐng)域相關(guān)邏輯都分離到Model中。View和Model完全解除耦合,不再像MVC中實現(xiàn)一個Observer模式,兩者的通信則依靠Presenter進行。Presenter響應(yīng)View接獲的用戶動作,并調(diào)用Model中的業(yè)務(wù)邏輯,最后將用戶需要的信息返回給View。 下圖直觀表示了MVP模式:圖2、MVP模式 圖2清楚地展示了MVP模式的幾個特點: 1、View和Model完全解耦,兩者不發(fā)生直接關(guān)聯(lián),通過Presenter進行通信。 2、Presenter并不是與具體的View耦合,而是和一個抽
7、象的View Interface耦合,View Interface相當(dāng)于一個契約,抽象出了對應(yīng)View應(yīng)實現(xiàn)的方法。只要實現(xiàn)了這個接口,任何View都可以與指定Presenter兼容,從而實現(xiàn)了P Logic的復(fù)用性和視圖的無縫替換。 3、View在MVP里應(yīng)該是一個“極瘦”的概念,最多也只能包含維護自身狀態(tài)的邏輯,而其它邏輯都應(yīng)實現(xiàn)在Presenter中。 總的來說,使用MVP模式可以得到以下兩個收益: 1、將UI和P Logic兩個關(guān)注點分離,得到更干凈和單一的代碼結(jié)構(gòu)。 2、實現(xiàn)了P Logic的復(fù)用以及View的無縫替換。在.NET平臺上實現(xiàn)MVP模式 這一節(jié)通過一個示例程序展示在.N
8、ET平臺上實現(xiàn)MVP的一種實踐方法。本來想通過我目前負責(zé)的實際項目中的代碼片段作為Demo,但這樣做存在兩個問題:一是這樣做可能會違反學(xué)校的保密守則,二是這個項目應(yīng)用了許多其他框架和模式,如通過Unity實現(xiàn)依賴注入,通過PostSharp實現(xiàn)AOP來負責(zé)異常處理和事務(wù)管理等,通過NHibernate實現(xiàn)的ORM等等,這樣如果讀者不了解系統(tǒng)整體架構(gòu)就很難完全讀懂代碼片段,MVP模式不夠突出。因此,我專門為這篇文章實現(xiàn)了一個Demo,其中的MVP實踐方式與實際項目中是一致的,而且Demo規(guī)模小,排除了其他干擾,使得讀者更容易理解其中的MVP實現(xiàn)方式。 這個簡單的Demo運行效果如下:圖3、Dem
9、o界面 這個Demo的功能如下:這是一個簡單的點餐軟件。系統(tǒng)中存有餐廳所有菜品的信息,客戶只需在界面右側(cè)輸入菜品名稱和數(shù)量,單擊“添加”按鈕,菜品就會被添加到左側(cè)點餐列表,并顯示此菜品詳細信息。如果所點菜品不存在則軟件會給出提示。另外,在左側(cè)已點餐品列表中右鍵單擊某個條目,在彈出菜單中點擊“刪除”,則可將此菜品從列表刪除。 下面分步驟介紹應(yīng)用了MVP模式的實現(xiàn)方式。第一步,解決方法及工程結(jié)構(gòu) 這個Demo共有三個工程,MVPSimple.Model為Mock方式實現(xiàn)的Services,作為Model;MVPSimple.Presenters為Presenter工程,其中包括Presenter和
10、View Interface;MVPSimple.WinUI為View的Windows Forms實現(xiàn)。第二步,構(gòu)建Mock方式的Services 因為重點在于表示層,所以這里的Services使用了Mock方式,并沒有包含真正的業(yè)務(wù)領(lǐng)域邏輯。其中MVPSimple.Model工程里兩個文件的代碼如下: FoodDto.cs:view source print?01using System; 0203namespace MVPSimple.Model 04 05/ 06/ 表示菜品類別的枚舉類型 07/ 08public enum FoodType 09 10主菜 = 1, 11湯 = 2,
11、12甜品 = 3, 13 1415/ 16/ 菜品的Data Transfer Object 17/ 18public class FoodDto 19 20/ 21/ ID,標(biāo)識字段 22/ 23public Int32 ID get; set; 2425/ 26/ 菜品名稱 27/ 28public String Name get; set; 2930/ 31/ 菜品類型 32/ 33public FoodType Type get; set; 3435/ 36/ 菜品價格 37/ 38public Double Price get; set; 3940/ 41/ 點菜數(shù)量 42/ 43p
12、ublic Int32 Amount get; set; 44 45 FoodServices.cs:view source print?01using System; 02using System.Collections.Generic; 0304namespace MVPSimple.Model 05 06/ 07/ 菜品Services的Mock實現(xiàn) 08/ 09public class FoodServices 10 11private IList foodList = new List(); 1213/ 14/ 默認構(gòu)造函數(shù),初始化各個菜品 15/ 16public FoodServ
13、ices() 17 18this.foodList.Add( 19new FoodDto() 20 21ID = 1, 22Name = 牛排, 23Price = 60.00, 24Type = FoodType.主菜, 25 26); 2728this.foodList.Add( 29new FoodDto() 30 31ID = 2, 32Name = 法式蝸牛, 33Price = 120.00, 34Type = FoodType.主菜, 35 36); 3738this.foodList.Add( 39new FoodDto() 40 41ID = 3, 42Name = 水果沙拉
14、, 43Price = 58.00, 44Type = FoodType.甜品, 45 46); 4748this.foodList.Add( 49new FoodDto() 50 51ID = 4, 52Name = 奶油紅菜湯, 53Price = 15.00, 54Type = FoodType.湯, 55 56); 5758this.foodList.Add( 59new FoodDto() 60 61ID = 5, 62Name = 雜拌湯, 63Price = 20.00, 64Type = FoodType.湯, 65 66); 67 6869/ 70/ 按照菜品名稱獲取菜品詳細
15、信息 71/ 72/ 菜品名稱 73/ 含有指定菜品信息的DTO 74public FoodDto GetFoodDetailByName(String foodName) 75 76foreach (FoodDto f in this.foodList) 77 78if (f.Name.Equals(foodName) 79 80return f; 81 82 8384return new FoodDto() ID = 0 ; 85 86 87第三步,通過View Interface規(guī)定View契約如果想實現(xiàn)Presenter和View的交互和無縫替換,必須在它們之間規(guī)定一個契約。一般來說,
16、每一張界面(注意是界面不是視圖)都應(yīng)該對應(yīng)一個View接口,不過由于Demo只有一個頁面,所以也只有一個View接口。 這里需要特別強調(diào),View接口必須抽象于任何具體視圖而服務(wù)于Presenter,所以,View接口中絕不能出現(xiàn)任何與具體視圖相關(guān)的元素。例如,我們的Demo中是使用Windows Forms作為視圖實現(xiàn),但View接口中絕不可出現(xiàn)與Windows Forms相耦合的元素,如返回一個Winform的TextBox。因為如果這樣做的話,使用其他技術(shù)實現(xiàn)的View就無法實現(xiàn)這個接口了,如使用Web Forms實現(xiàn),而Web Forms是不可能返回一個Winform的TextBox的
17、。 下面給出視圖接口的代碼。 IMainView.cs:view source print?01using System; 02using System.Collections.Generic; 03using MVPSimple.Model; 0405namespace MVPSimple.Presenters 06 07/ 08/ MainView的接口,所有MainView必須實現(xiàn)此接口,此接口暴露給Presenter 09/ 10public interface IMainView 11 12/ 13/ View上的菜品名稱 14/ 15String foodName get; set
18、; 1617/ 18/ View上點菜數(shù)量 19/ 20Int32 Amount get; set; 2122/ 23/ 判斷某一菜品是否已經(jīng)存在于點菜列表中 24/ 25/ 菜品名稱 26/ 結(jié)果 27bool IsExistInList(String foodName); 2829/ 30/ 將某一菜品加入點菜列表 31/ 32/ 菜品DTO 33void AddFoodToList(FoodDto food); 3435/ 36/ 將某一已點菜品從列表中移除 37/ 38/ 欲移除的菜品名稱 39void RemoveFoodFromList(String foodName); 4041
19、/ 42/ View顯示提示信息給用戶 43/ 44/ 信息內(nèi)容 45void ShowMessage(String message); 4647/ 48/ View顯示確認信息并返回結(jié)果 49/ 50/ 信息內(nèi)容 51/ 用戶回答是確定還是取消。True - 確定,F(xiàn)alse - 取消 52bool ShowConfirm(String message); 53 54 可以看到,IMainView抽象了如圖3所示的界面,但又不包含任何與Windows Forms相耦合的元素,因此如果需要,以后完全可以使用Web Forms、WPF或SL等技術(shù)實現(xiàn)這個接口。第四步,實現(xiàn)Presenter 上文
20、說過,一個界面應(yīng)該對應(yīng)一個Presenter,這個Demo里只有一個界面,所以只有一個Presenter。Presenter僅于視圖接口耦合,而并不和具體視圖耦合,最好證據(jù)就是Presenter工程根本沒有引用WinUI工程!代碼如下: MainPresenter.cs:view source print?01using System; 02using System.Collections.Generic; 03using MVPSimple.Model; 0405namespace MVPSimple.Presenters 06 07/ 08/ MainView的Presenter 09/
21、10public class MainPresenter 11 12/ 13/ 當(dāng)前關(guān)聯(lián)View 14/ 15public IMainView View get; set; 1617/ 18/ 默認構(gòu)造函數(shù),初始化View 19/ 20/ MainView對象 21public MainPresenter(IMainView view) 22 23View = view; 24 2526#region Acitons 2728/ 29/ Action:將所點菜品增加到點菜列表 30/ 31public void AddFoodAction() 32 33if (String.IsNullOrE
22、mpty(View.foodName) 34 35View.ShowMessage(請選輸入菜品名稱); 36return; 37 38if (View.Amount = 0) 39 40View.ShowMessage(點菜的份數(shù)至少要是一份); 41return; 42 43if (View.IsExistInList(View.foodName) 44 45View.ShowMessage(String.Format(菜品【0】已經(jīng)在您的菜單中, View.foodName); 46return; 47 4849FoodServices foodServ = new FoodServic
23、es(); 50FoodDto food = foodServ.GetFoodDetailByName(View.foodName); 51if (food.ID = 0) 52 53View.ShowMessage(String.Format(抱歉,本餐廳沒有菜品【0】,View.foodName); 54return; 55 5657View.AddFoodToList(food); 58 5960/ 61/ Action:從點菜列表移除某一菜品 62/ 63/ 被移除菜品的名稱 64public void RemoveFoodAction(String foodName) 65 66if
24、 (View.ShowConfirm(確定要刪除嗎?) 67 68View.RemoveFoodFromList(foodName); 69 70 7172#endregion 73 74第五步,實現(xiàn)View 這里我們使用Windows Forms實現(xiàn)View。如果朋友們有興趣,完全可以自己試著用Web或WPF實現(xiàn)以下視圖,同時可以驗證P Logic的可復(fù)用性和視圖無縫替換,親身體驗一下MVP模式的威力。Winform的View代碼如下。 frmMain.cs:view source print?001using System; 002using System.Windows.Forms; 0
25、03using MVPSimple.Model; 004using MVPSimple.Presenters; 005006namespace MVPSimple.WinUI 007 008/ 009/ MainView的Windows Forms實現(xiàn) 010/ 011public partial class frmMain : Form, IMainView 012 013/ 014/ 相關(guān)聯(lián)的Presenter 015/ 016private MainPresenter presenter; 017018/ 019/ 默認構(gòu)造函數(shù),初始化Presenter 020/ 021public f
26、rmMain() 022 023InitializeComponent(); 024this.presenter = new MainPresenter(this); 025 026027#region IMainView Members 028029/ 030/ View上的菜品名稱 031/ 032public String foodName 033 034get return this.tbFoodName.Text; 035set this.tbFoodName.Text = value; 036 037038/ 039/ View上點菜數(shù)量 040/ 041public Int32
27、Amount 042 043get return (Int32)this.tbAmount.Value; 044set this.tbAmount.Value = (Decimal)value; 045 046047/ 048/ 判斷某一菜品是否已經(jīng)存在于點菜列表中 049/ 050/ 菜品名稱 051/ 結(jié)果 052public bool IsExistInList(String foodName) 053 054foreach (ListViewItem i in this.lvFoods.Items) 055 056if (i.Text = foodName) 057 058return
28、 true; 059 060 061062return false; 063 064065/ 066/ 將某一菜品加入點菜列表 067/ 068/ 菜品DTO 069public void AddFoodToList(FoodDto food) 070 071ListViewItem item = new ListViewItem(); 072Double price = food.Price * (Double)this.tbAmount.Value; 073074item.Text = food.Name; 075item.SubItems.Add(food.Type.ToString()
29、; 076item.SubItems.Add(this.tbAmount.Value.ToString(); 077item.SubItems.Add(price.ToString(); 078this.lvFoods.Items.Add(item); 079 080081/ 082/ 將某一已點菜品從列表中移除 083/ 084/ 欲移除的菜品名稱 085public void RemoveFoodFromList(String foodName) 086 087foreach (ListViewItem i in this.lvFoods.Items) 088 089if (i.Text = foodName) 090 091this.lvFoods.Items.Remove(i); 092 093 094 095096/ 097/ View顯示提示信息給用戶 098/ 099/ 信息內(nèi)容 100public void ShowMessage(String message) 101 102MessageBox.Show(message, 信息, MessageBoxButtons.OK, MessageBoxIcon.Warning); 103 104105/ 106/ View顯示確認信息并返回結(jié)果 10
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 美發(fā)店員工合同范本(7篇)
- 2024-2025學(xué)年第2課諸侯紛爭與變法運動-勤徑學(xué)升高中歷史必修上同步練測(統(tǒng)編版2019)
- 2025年企業(yè)市場營銷合作伙伴協(xié)議
- 2025年酒店客房用品訂購合同模板
- 2025年不動產(chǎn)權(quán)益讓與擔(dān)保協(xié)議版
- 2025年電動車維修服務(wù)合同示范
- 2025年水文測量儀器項目立項申請報告模范
- 2025年企業(yè)銷售專員合同格式
- 2025年戀愛雙方保密協(xié)議策劃模板
- 2025年度股權(quán)變更持有人協(xié)議
- 沈陽市第一屆“舒心傳技 莘紳向陽”職業(yè)技能大賽技術(shù)工作文件-27-全媒體運營師
- 2025年《贏在執(zhí)行》心得體會(4篇)
- 2025年華潤燃氣投資中國有限公司招聘筆試參考題庫含答案解析
- 2025年多彩貴州航空有限公司招聘筆試參考題庫含答案解析
- 安全生產(chǎn)網(wǎng)格員培訓(xùn)
- 2025年江蘇泰州市住房和城鄉(xiāng)建設(shè)局下屬事業(yè)單位公開招聘工作人員歷年高頻重點提升(共500題)附帶答案詳解
- 深圳建筑工程公司財務(wù)管理制度
- 小紅書搜索推廣營銷師認證考試題(附答案)
- 符合TSG07-2019 B級鍋爐制造質(zhì)量手冊首版
- 統(tǒng)編版語文三年級下冊第三單元綜合性學(xué)習(xí)中華傳統(tǒng)節(jié)日 活動設(shè)計
- 降低順產(chǎn)產(chǎn)婦產(chǎn)后2小時失血率PDCA成果匯報書
評論
0/150
提交評論