<span id="mktg5"></span>

<i id="mktg5"><meter id="mktg5"></meter></i>

        <label id="mktg5"><meter id="mktg5"></meter></label>
        最新文章專題視頻專題問答1問答10問答100問答1000問答2000關鍵字專題1關鍵字專題50關鍵字專題500關鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關鍵字專題關鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
        問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
        當前位置: 首頁 - 科技 - 知識百科 - 正文

        淺析MVP模式中V-P交互問題及案例分享

        來源:懂視網 責編:小采 時間:2020-11-27 22:39:01
        文檔

        淺析MVP模式中V-P交互問題及案例分享

        淺析MVP模式中V-P交互問題及案例分享:在差不多兩年的時間內,我們項目組幾十來號人都撲在一個項目上面。這是一個基于微軟SCSF(Smart Client Software Factory)的項目,客戶端是墨爾本一家事業單位。前兩周,我奉命負責對某個模塊進行Code Review工作,在此期間,發現了一些問題,也有了一些想法
        推薦度:
        導讀淺析MVP模式中V-P交互問題及案例分享:在差不多兩年的時間內,我們項目組幾十來號人都撲在一個項目上面。這是一個基于微軟SCSF(Smart Client Software Factory)的項目,客戶端是墨爾本一家事業單位。前兩周,我奉命負責對某個模塊進行Code Review工作,在此期間,發現了一些問題,也有了一些想法

        在差不多兩年的時間內,我們項目組幾十來號人都撲在一個項目上面。這是一個基于微軟SCSF(Smart Client Software Factory)的項目,客戶端是墨爾本一家事業單位。前兩周,我奉命負責對某個模塊進行Code Review工作,在此期間,發現了一些問題,也有了一些想法。不過,有些想法可能還不是很成熟,不能完全保證其正確性,有機會寫出來討論一下。今天來說說關于MVP的一些想法。

        一、簡單講講MVP是什么玩意兒
        如果從層次關系來講,MVP屬于Presentation層的設計模式。對于一個UI模塊來說,它的所有功能被分割為三個部分,分別通過Model、View和Presenter來承載。Model、View和Presenter相互協作,完成對最初數據的呈現和對用戶操作的響應,它們具有各自的職責劃分。Model可以看成是模塊的業務邏輯和數據的提供者;View專門負責數據可視化的呈現,和用戶交互事件的相對應。一般地,View會實現一個相應的接口;Presenter是一般充當Model和View的紐帶。

        MVP具有很多的變體,其中最為常用的一種變體成為Passive View(被動視圖)。對于Passive View,Model、View和Presenter之間的關系如下圖所示。View和Modell之間不能直接交互,View通過Presenter與Model打交道。Presenter接受View的UI請求,完成簡單的UI處理邏輯,并調用Model進行業務處理,并調用View將相應的結果反映出來。View直接依賴Presenter,但是Presenter間接依賴View,它直接依賴的是View實現的接口。關于MVP和Passive View基本的常識性東西,不是本篇文章論述的重點,對此不清楚的讀者相信可以Google出很多相關的資料來,所以在這里就再多做介紹了。

        二、Passive View模式的基本特征總結

        Passive View,顧名思義,View是被動的。那么主動是誰呢?答案是Presenter。對于Presenter的主動性,我個人是這么理解的:

        •Presenter是整個MVP體系的控制中心,而不是單純的處理View請求的人;
        •View僅僅是用戶交互請求的匯報者,對于響應用戶交互相關的邏輯和流程,View不參與決策,真正的決策者是Presenter;
        •View向Presenter發送用戶交互請求應該采用這樣的口吻:“我現在將用戶交互請求發送給你,你看著辦,需要我的時候我會協助你”,不應該是這樣:“我現在處理用戶交互請求了,我知道該怎么辦,但是我需要你的支持,因為實現業務邏輯的Model只信任你”;
        •對于綁定到View上的數據,不應該是View從Presenter上“拉”回來的,應該是Presenter主動“推”給View的;
        •View盡可能不維護數據狀態,因為其本身僅僅實現單純的、獨立的UI操作;Presenter才是整個體系的協調者,它根據處理用于交互的邏輯給View和Model安排工作。

        三、理想與現實的距離

        上面對Passive View MVP特征的羅列,我覺得是一種理想狀態。是在大型項目中,尤其是項目的開發者自身并不完全理解MVP原理的情況下,要整體實現這樣的一種理想狀態是一件很難的事情。有人可能會說,在開發人員不了解MVP的情況下要求他們用好MVP,你這不是扯淡嗎?實際上,在這里并不是說開發人員完全沒有MVP關于關注點分離的概念,只是對MVP中的三元角色并沒有非常清晰的界定(實際上也沒有一個明確的規范對Model、View和Presenter具體的職責范圍進行明確的劃分),在開發的時候,會不自覺地受傳統編程習慣的影響,將Presenter單純地當成是View調用Model的中介。我經常這么說:如果以View為中心,將Presenter當成是View和Model的中間人,這也叫MVP模式,不過這里的P不是Presenter,而是Proxy,是Model在View的代理而已。

        從Passive View中Model、View和Presenter三者之間的依賴關系來看,這個模型充分地給了開發者犯這樣錯誤的機會。注意上面的圖中View到Presenter的箭頭表明View是可以任意的調用Presenter的。開發人員完全有可能將大部分UI處理邏輯寫在View中,而Presenter僅僅對Model響應操作的簡單調用。因為在我Review的各種所謂的MVP編程方式中,有不少是這么寫的。在很多情況下,甚至不用認真去分析具體的代碼,從View和Presenter中代碼的行數就可以看出來,因為View的代碼和Presenter的代碼都不在一個數量級。

        我現在的一個目的是提出一種編程模式,杜絕開發人員將程序寫成基于Proxy的MVP,在我看來,唯一的辦法就是盡量弱化(不可能剔除)View對Presenter的依賴。實際上,對于MVP來說,View僅僅向Presenter遞交用戶交互請求,僅此而已。如果我們將View對Presenter的這點依賴關系實現在框架層次中,最終開發人員的編程來說就不需要這種依賴了。那么我就可以通過一定的編程技巧使View根本無法訪問Presenter,從而避免Presenter成為Proxy的可能的。

        那么,如果在不能獲得Presenter的情況下,使View能夠正常將請求遞交給Presenter呢?很簡單,通過事件訂閱機制就可以了,雖然View不可以獲取到Presenter,但是Presenter卻可以獲取到View,讓Presenter訂閱View的相關事件就可以的。

        四、讓View不再依賴Presenter的編程模型

        現在,我們就來如果通過一種簡單的編程模式就能夠讓View對Presenter的依賴完全地從中最終開發者的源代碼中移除。為此,我們需要定義一系列的基類,首先我為所有的View創建基類ViewBase,在這里我們直接用Form作為View,而在SCSF中View一般是通過UserControl來表示的。ViewBase定義如下,為了使View中不能調用Presenter,我將其定義成私有字段。那么,如何讓View和Presenter之間建立起關聯呢?在這里通過虛方法CreatePresenter,具體的View必須重寫該方法,不然會拋出一個NotImplementedException異常。在構造函數中,調用該方法比用返回值為Presenter賦值。

        代碼如下:
         using System;
         using System.ComponentModell;
         using System.Windows.Forms;
         namespace MVPDemo
         {
             public class ViewBase: Form
             {
                 private object _presenter;

                 public ViewBase()
                 {
                     _presenter = this.CreatePresenter();
                 }

                 protected virtual object CreatePresenter()
                 {
                     if (LicenseManager.CurrentContext.UsageModel == LicenseUsageModel.Designtime)
                     {
                         return null;
                     }
                     else
                     {
                         throw new NotImplementedException(string.Format("{0} must override the CreatePresenter method.", this.GetType().FullName));
                     }
                 }      
             }
         }

        然后,我們也為所有的Presenter創建基類Presenter<IView>,泛型類型IView表示具體View實現的接口。表示View的同名只讀屬性在構造函數中賦值,賦值完成之后調用調用虛方法OnViewSet。具體的Presenter可以重寫該方法進行對View進行事件注冊工作。但是需要注意的是,Presenter的創建是在ViewBase的構造函數中通過調用CreatePresenter方法實現,所以執行OnViewSet的時候,View本身還沒有完全初始化,所以在此不能對View的控件進行操作。

        代碼如下:
         namespace MVPDemo
         {
             public class Presenter<IView>
             {
                 public IView View { get; private set; }

                 public Presenter(IView view)
                 {
                     this.View = view;
                     this.OnViewSet();
                 }
                 protected virtual void OnViewSet()
                 { }
             }
         }

        由于,Presenter是通過接口的方式與View進行交互的。在這里,由于View通過Form的形式體現,有時候我們要通過這個接口訪問Form的一些屬性、方法和事件,需要將相應的成員定義在接口上面,比較麻煩。此時,我們可以選擇將這些成員定義在一個接口中,具體View的接口繼承該接口就可以了。在這里,我們相當是為所有的View接口創建了“基接口”。作為演示,我現在了Form的三個事件成員定義在街口IViewBase中。

        代碼如下:
         using System;
         using System.ComponentModell;
         namespace MVPDemo
         {
            public interface IViewBase
             {
                event EventHandler Load;
                event EventHandler Closed;
                event CancelEventHandler Closing;
             }
         }

        五、實例演示

        上面我通過定義基類和接口為整個編程模型搭建了一個框架,現在我們通過一個具體的例子來介紹該編程模型的應用。我們采用的是一個簡單的Windows Forms應用,模擬管理客戶信息的場景,邏輯很簡單:程序啟動的時候顯示出所有的客戶端列表;用戶選擇某一客戶端,將響應的信息顯示在TextBox中以供編輯;對客戶端信息進行相應修改之后,點擊OK按鈕進行保存。整個操作界面如下圖所示:


        首先,我們創建實體類Customer,簡單起見,僅僅包含四個屬性:Id、FirstName、LastName和Address:

        代碼如下:
         using System;
         namespace MVPDemo
         {
             public class Customer: ICloneable
             {
                 public string Id
                 { get; set; }

                 public string FirstName
                 { get; set; }

                 public string LastName
                 { get; set; }

                 public string Address
                 { get; set; }      

                 object ICloneable.Clone()
                 {
                     return this.Clone();
                 }

                 public Customer Clone()
                 {
                     return new Customer {
                         Id          = this.Id,
                         FirstName   = this.FirstName,
                         LastName    = this.LastName,
                         Address     = this.Address
                     };
                 }
             }
         }

        然后,為了真實模擬MVP三種角色,特意創建一個CustomerModel類型,實際上在真實的應用中,并沒有單獨一個類型來表示Model。CustomerModel維護客戶列表,體統相關的查詢和更新操作。CustomerModel定義如下:

        代碼如下:
         using System.Collections.Generic;
         using System.Linq;
         namespace MVPDemo
         {
             public class CustomerModel
             {
                 private IList<Customer> _customers = new List<Customer>{
                     new Customer{ Id = "001", FirstName = "San", LastName = "Zhang", Address="Su zhou"},
                     new Customer{ Id = "002", FirstName = "Si", LastName = "Li", Address="Shang Hai"}
                 };

                 public void UpdateCustomer(Customer customer)
                 {
                     for (int i = 0; i < _customers.Count; i++)
                     {
                         if (_customers[i].Id == customer.Id)
                         {
                             _customers[i] = customer;
                             break;
                         }
                     }
                 }

                 public Customer GetCustomerById(string id)
                 {
                     var customers = from customer in _customers
                                     where customer.Id == id
                                     select customer.Clone();
                     return customers.ToArray<Customer>()[0];
                 }

                 public Customer[] GetAllCustomers()
                 {
                     var customers = from customer in _customers
                                     select customer.Clone();
                     return customers.ToArray<Customer>();
                 }
             }
         }

        接著,我們定義View的接口ICustomerView。ICustomerView定義了兩個事件,CustomerSelected在用戶從Gird中選擇了某個條客戶記錄是觸發,而CustomerSaving則在用戶完成編輯點擊OK按鈕視圖提交修改時觸發。ICustomerView還定義了View必須完成的三個基本操作:綁定客戶列表(ListAllCustomers);顯示單個客戶信息到TextBox(DisplayCustomerInfo);保存后清空可編輯控件(Clear)。

        代碼如下:
         using System;
         namespace MVPDemo
         {
             public interface ICustomerView : IViewBase
             {
                 event EventHandler<CustomerEventArgs> CustomerSelected;

                 event EventHandler<CustomerEventArgs> CustomerSaving;

                 void ListAllCustomers(Customer[] customers);

                 void DisplayCustomerInfo(Customer customer);

                 void Clear();
             }
         }

        事件參數的類型CustomerEventArgs定義如下,兩個屬性CustomerId和Customer分別代表客戶ID和具體的客戶,它們分別用于上面提到的CustomerSelected和CustomerSaving事件。

        代碼如下:
         using System;
         namespace MVPDemo
         {
             public class CustomerEventArgs : EventArgs
             {
                 public string CustomerId
                 { get; set; }

                 public Customer Customer
                 { get; set; }
             }
         }

        而具體的Presenter定義在如下的CustomerPresenter類型中。在重寫的OnViewSet方法中注冊View的三個事件:Load事件中調用Model獲取所有客戶列表,并顯示在View的Grid上;CustomerSelected事件中通過事件參數傳遞的客戶ID調用Model獲取相應的客戶信息,顯示在View的可編輯控件上;CustomerSaving則通過事件參數傳遞的被更新過的客戶信息,調用Model提交更新。

        代碼如下:
         using System.Windows.Forms;

         namespace MVPDemo
         {  
             public class CustomerPresenter: Presenter<ICustomerView>
             {
                 public CustomerModel Model
                 { get; private set; }

                 public CustomerPresenter(ICustomerView view)
                     : base(view)
                 {
                     this.Model = new CustomerModel();
                 }

                 protected override void OnViewSet()
                 {
                     this.View.Load += (sender, args) =>
                         {
                             Customer[] customers = this.Model.GetAllCustomers();
                             this.View.ListAllCustomers(customers);
                             this.View.Clear();
                         };
                     this.View.CustomerSelected += (sender, args) =>
                         {
                             Customer customer = this.Model.GetCustomerById(args.CustomerId);
                             this.View.DisplayCustomerInfo(customer);
                         };
                     this.View.CustomerSaving += (sender, args) =>
                         {
                             this.Model.UpdateCustomer(args.Customer);
                             Customer[] customers = this.Model.GetAllCustomers();
                             this.View.ListAllCustomers(customers);
                             this.View.Clear();
                             MessageBox.Show("The customer has been successfully updated!", "Successfully Update", MessageBoxButtons.OK, MessageBoxIcon.Information);
                         };
                 }      
             }
         }

        對于具體的View來說,僅僅需要實現ICustomerView,并處理響應控件事件即可(主要是用戶從Grid中選擇某個記錄觸發的RowHeaderMouseClick事件,以及點擊OK的事件)。實際上不需要View親自處理這些事件,而僅僅需要觸發相應的事件,讓事件訂閱者(Presenter)來處理就可以了。此外還需要重寫CreatePresenter方法完成對CustomerPresenter的創建。CustomerView定義如下:

        代碼如下:
         using System;
         using System.Windows.Forms;

         namespace MVPDemo
         {
             public partial class CustomerView : ViewBase, ICustomerView
             {
                 public CustomerView()
                 {
                     InitializeComponent();           
                 }

                 protected override object CreatePresenter()
                 {
                     return new CustomerPresenter(this);
                 }

                 #region ICustomerView Members

                 public event EventHandler<CustomerEventArgs> CustomerSelected;

                 public event EventHandler<CustomerEventArgs> CustomerSaving;

                 public void ListAllCustomers(Customer[] customers)
                 {
                     this.dataGridViewCustomers.DataSource = customers;
                 }

                 public void DisplayCustomerInfo(Customer customer)
                 {
                     this.buttonOK.Enabled = true;
                     this.textBoxId.Text = customer.Id;
                     this.textBox1stName.Text = customer.FirstName;
                     this.textBoxLastName.Text = customer.LastName;
                     this.textBoxAddress.Text = customer.Address;
                 }

                 public void Clear()
                 {
                     this.buttonOK.Enabled       = false;
                     this.textBox1stName.Text    = string.Empty;
                     this.textBoxLastName.Text   = string.Empty;
                     this.textBoxAddress.Text    = string.Empty;
                     this.textBoxId.Text         = string.Empty;
                 }

                 #endregion

                 protected virtual void OnCustomerSelected(string customerId)
                 {
                     var previousId = this.textBoxId.Text.Trim();
                     if (customerId == previousId)
                     {
                         return;
                     }
                     if(null != this.CustomerSelected)
                     {
                         this.CustomerSelected(this, new CustomerEventArgs{ CustomerId = customerId});
                     }
                 }

                 protected virtual void OnCustomerSaving(Customer customer)
                 {
                     if(null != this.CustomerSaving)
                     {
                         this.CustomerSaving(this, new CustomerEventArgs{ Customer = customer});
                     }
                 }

                 private void dataGridViewCustomers_RowHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
                 {  
                     var currentRow = this.dataGridViewCustomers.Rows[e.RowIndex];
                     var customerId = currentRow.Cells[0].Value.ToString();
                     this.OnCustomerSelected(customerId);
                 }

                 private void buttonOK_Click(object sender, EventArgs e)
                 {
                     var customer        = new Customer();
                     customer.Id         = this.textBoxId.Text.Trim();
                     customer.FirstName  = this.textBox1stName.Text.Trim();
                     customer.LastName   = this.textBoxLastName.Text.Trim();
                     customer.Address    = this.textBoxAddress.Text.Trim();
                     this.OnCustomerSaving(customer);
                 }
             }
         }

        聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

        文檔

        淺析MVP模式中V-P交互問題及案例分享

        淺析MVP模式中V-P交互問題及案例分享:在差不多兩年的時間內,我們項目組幾十來號人都撲在一個項目上面。這是一個基于微軟SCSF(Smart Client Software Factory)的項目,客戶端是墨爾本一家事業單位。前兩周,我奉命負責對某個模塊進行Code Review工作,在此期間,發現了一些問題,也有了一些想法
        推薦度:
        • 熱門焦點

        最新推薦

        猜你喜歡

        熱門推薦

        專題
        Top
        主站蜘蛛池模板: 一级成人a毛片免费播放| 极品色天使在线婷婷天堂亚洲| igao激情在线视频免费| 国产精品va无码免费麻豆| 亚洲国产91在线| 一个人免费观看视频www| 亚洲国产成人精品无码一区二区 | 又粗又硬又大又爽免费视频播放| 亚洲视频无码高清在线| 在线观看成人免费视频| 小说区亚洲自拍另类| 亚洲精品国产高清嫩草影院| 国产精品无码免费专区午夜 | 一级做a毛片免费视频| 久久久久亚洲AV成人网| 免费无码又爽又刺激网站| 久久久久亚洲AV成人无码 | jjzz亚洲亚洲女人| 人妻仑乱A级毛片免费看| 亚洲高清专区日韩精品| 99在线在线视频免费视频观看| 亚洲国语在线视频手机在线| 好男人视频在线观看免费看片| AV激情亚洲男人的天堂国语| 久久影视国产亚洲| 久久国产精品成人片免费| 亚洲人成人无码.www石榴| 又大又硬又爽免费视频| a成人毛片免费观看| 亚洲国产精品综合久久久| 免费看无码自慰一区二区| 国产V片在线播放免费无码| 亚洲国产精品一区| 免费视频中文字幕| 精品国产免费人成网站| 亚洲va乱码一区二区三区| 国产成人亚洲综合无码| 日本免费xxxx色视频| 高清免费久久午夜精品| 亚洲成人高清在线观看| 亚洲区日韩区无码区|