下面是一個簡單的例子,表明了使用MVP設計圖案被動視圖的概念。因爲我們使用被動視圖,所以視圖不知道演示者。主持人只需訂閱該視圖發佈的事件並採取相應行動。
首先,我們需要爲我們的視圖定義合同。這通常是通過一個接口實現的,本質上,我們希望與我們的視圖有一個非常鬆散的耦合。我們希望能夠切換到不同的視圖或事件,爲單元測試創建模擬視圖。
這裏是描述將用來顯示客戶信息
public interface ICustomerManagementView
{
void InitializeCustomers(ICustomer[] customers);
void DisplayCustomer(ICustomer customer);
event EventHandler<EventArgs<ICustomer>> SelectedCustomerChanged;
}
它暴露了一個方法InitializeCustomers將被用來初始化我們從我們的模型對象視圖簡單視圖的合同。
我們還有一個事件SelectedCustomerChanged將由我們的演示者用來接收視圖中發生操作的通知。
一旦我們有了我們的合同,我們就可以開始在我們的演示者中處理這些互動。
public class CustomerManagementPresenter
{
private ICustomer _selectedCustomer;
private readonly ICustomerManagementView _managementView;
private readonly ICustomerRepository _customerRepository;
public CustomerManagementPresenter(ICustomerManagementView managementView, ICustomerRepository customerRepository)
{
_managementView = managementView;
_managementView.SelectedCustomerChanged += this.SelectedCustomerChanged;
_customerRepository = customerRepository;
_managementView.InitializeCustomers(_customerRepository.FetchCustomers());
}
private void SelectedCustomerChanged(object sender, EventArgs<ICustomer> args)
{
// Perform some logic here to update the view
if(_selectedCustomer != args.Value)
{
_selectedCustomer = args.Value;
_managementView.DisplayCustomer(_selectedCustomer);
}
}
}
在我們可以使用一個叫做dependency injection提供訪問我們的觀點,並且我們可能需要的任何模型類的另一個設計模式的演講。在這個例子中,我有一個負責獲取客戶細節的CustomerRepository。
在構造函數中,我們有兩個重要的代碼行,首先我們在視圖中訂閱了SelectedCustomerChanged事件,在這裏我們可以執行相關的操作。其次,我們用存儲庫中的數據調用了InitilaizeCustomers。
在這一點上我們還沒有爲我們的視圖實際定義具體的實現,我們需要做的就是創建一個實現ICustomerManagementView的對象。例如,在Windows窗體應用程序,我們可以做以下
public partial class CustomerManagementView : Form, ICustomerManagementView
{
public CustomerManagementView()
{
this.InitializeComponents();
}
public void InitializeCustomers(ICustomer[] customers)
{
// Populate the tree view with customer details
}
public void DisplayCustomer(ICustomer customer)
{
// Display the customer...
}
// Event handler that responds to node selection
private void CustomerTreeViewAfterSelect(object sender, TreeViewEventArgs e)
{
var customer = e.Node.Tag as ICustomer;
if(customer != null)
{
this.OnSelectedCustomerChanged(new EventArgs<ICustomer>(customer));
}
}
// Protected method so that we can raise our event
protected virtual void OnSelectedCustomerChanged(EventArgs<ICustomer> args)
{
var eventHandler = this.SelectedCustomerChanged;
if(eventHandler != null)
{
eventHandler.Invoke(this, args);
}
}
// Our view will raise an event each time the selected customer changes
public event EventHandler<EventArgs<ICustomer>> SelectedCustomerChanged;
}
如果我們想測試我們的表現邏輯,我們可以嘲笑我們的觀點,並執行一些斷言。
編輯:包括自定義事件參數
public class EventArgs<T> : EventArgs
{
private readonly T _value;
public EventArgs(T value)
{
_value = value;
}
public T Value
{
get { return _value; }
}
}
我很欣賞信息豐富的答覆,但它確實解決了我的問題。我是否應該使用一個View(用戶可以與這些4-5個子組件交互),還是應該在Views中有Views(因此接口內的接口)?此外,我仍然不確定將視圖中的事件「交付給」演示者,我仍然需要澄清View如何觀察模型。我也很關心程序的「結構」。 – 2010-11-30 21:43:26
我更喜歡把它分解成更小的組件,特別是如果你想鼓勵重用。創建具有單一用途的視圖,但可以一起使用以創建複合UI – 2010-11-30 21:51:18