我一直在寻找MVP模式一段时间,并设法创建一些简单的MVP兼容应用程序。
我现在正在尝试将模式应用于更复杂的应用程序,我对这样做的最佳方式有一些疑问。
我的应用程序有一个WinForm,有两个按钮用于加载两种不同类型的数据。我的视图界面如下所示:
interface IView_MainForm
{
// Load input
//
event EventHandler<InputLoadEventArgs> LoadInput_01;
event EventHandler<InputLoadEventArgs> LoadInput_02;
bool Input01_Loaded { get; set; }
bool Input02_Loaded { get; set; }
}
IView通过构造函数注入在我的演示者中引用:
public Presenter_MainForm(IView_MainForm view)
{
this.View = view;
this.View.LoadInput_01 += new EventHandler<InputLoadEventArgs>(OnLoadInput_01);
this.View.LoadInput_02 += new EventHandler<InputLoadEventArgs>(OnLoadInput_02);
}
到目前为止,这么好。当用户单击两个按钮中的任何一个来加载数据时,会引发一个LoadInput _ ##事件,Presenter正在处理它,检查输入是否有错误并根据我的数据模型对其进行构造。
我的下一步是在View中显示已处理的数据。
我正努力让我的视图尽可能被动和“愚蠢”,假设它对Presenter一无所知(它没有订阅它的事件,Presenter通过调用IView方法将数据发送到View) ,更不用说模特了。
如果View不知道数据模型是什么样的,我应该如何填充像TreeView这样的控件呢?
另外,我是否将整个MVP视为正确,或者我有什么遗漏?
答案 0 :(得分:1)
在complex type
中拥有View
属性没有任何问题。假设您有一些ComplexType
。
class ComplexType
{
public string ParentNode {get;set;}
public List<string> ChildNodes {get;set;}
// some other properties
}
我们还假设ComplexType
是您TreeView
的数据模型。使用MVP
模式完全没有问题可以使View
上的属性具有ComplexType
。所以有这样的东西是完全没问题的
interface IView_MainForm
{
// Load input
//
event EventHandler<InputLoadEventArgs> LoadInput_01;
event EventHandler<InputLoadEventArgs> LoadInput_02;
bool Input01_Loaded { get; set; }
bool Input02_Loaded { get; set; }
ComplexType Input01Data {get;set;} // you might actually have some code in get/set setters
ComplexType Input02Data {get;set;} // you might actually have some code in get/set setters
public void SetInput01Data(ComplexType input01Data)
{
Input01Data = input01Data;
// some other stuff
}
}
由于您的Model
适用于View
,其中包含2个输入,因此您的模型可能看起来像这样
public interface IModel
{
public ComplexType Input01Data {get;set;}
public ComplexType Input02Data {get;set;}
}
现在,在您的Presenter
中,您只需处理从View
触发的事件,填充Model
并在View
上设置属性
class Presenter
{
private IModel _myModel...
private IRepository _repository;
public Presenter(IView_MainForm view, IRepository repository)
{
_repository = repository;
this.View = view;
this.View.LoadInput_01 += new EventHandler<InputLoadEventArgs>(OnLoadInput_01);
this.View.LoadInput_02 += new EventHandler<InputLoadEventArgs>(OnLoadInput_02);
}
public void OnLoadInput_01(object sender, InputLoadEventArgs e)
{
// get data based on passed arguments (e.SomeProperty)
// construct IModel
myModel = _repository.GetData(e.SomeProperty);
// pass data to IView_MainForm
View.SetInput01Data(myModel.Input01Data);
}
}
关于你的关注
我努力让自己的观点尽可能地被动和“愚蠢”, 假设它对Presenter一无所知(它没有订阅 它的事件,Presenter通过调用IView将数据发送到View 而不是模型。
您的View
仍然不了解Presenter
和Model
。它只是触发事件,从Presenter
获取数据并绑定其控件。你有testability
(请注意这个单元测试是伪代码,因为我不知道你如何检索数据,你在按钮点击事件中需要什么输入......)。
[Test]
public void ShouldLoadInput01DataOnButtonClick()
{
// Arrange
IModel data = // create dummy data
Mock<IView_MainForm> clientsViewMock = new Mock<IView_MainForm>();
Mock<IRepository> clientsRepositoryMock = new Mock<IRepository>();
clientsRepositoryMock.Setup(repository => repository.GetData(something)).Returns(data.Input01Data);
var presenter = new Presenter(clientsViewMock.Object, clientsRepositoryMock .Object);
// Act
clientsViewMock.Raise(view => view.LoadInput01 += null, new InputLoadEventArgs());
// Assert
clientsViewMock.Verify(view => view.SetInput01Data(data.Input01Data), "Input01 data expected be set on button click.");
}