myRoad—关于MVP模式
一,MVP模式
认识MVP模式是通过读artech的文章,在工作中使用MVP模式也是时刻想着他翻译的的CAB(Componsable Application Block)基于14条MVP规则:
- 所有的View(包括View的接口)的名称应该以View作为后缀,比如TaskView/ITaskView;
- 所有的Presenter名称应该以Presenter作为后缀,比如TaskViewPresenter;
- Presenter完成Use Case处理逻辑,对GUI控件的处理应该在View中实现;
- View调用Presenter的方法应该像触发事件异常,通过调用OnXxx方法的方式来实现;
- 应该尽可能地限制View对Presenter的调用,并且调用的方式限于按照“事件”的形式,比如_presenter.OnViewReady();
- View不允许通过Presenter直接调用Model和Service,并且Presenter的方法应该是不具有返回值的;
- Presenter必须通过View接口的方式调用View
- 除了对View接口成员的实现外,View中的其他方法不应该是public的;
- 除了CAB ModuleController 对View的加载和限制外,View只能被Presenter调用;
- View接口方法应该基于Use Case的逻辑起一个有意义的名称,比如SetDataSource这样的方法名称是不合法的;
- View接口的成员应该仅限于方法,不应该包含属性;
- 所有的数据应用保持在Model中
- 定义在View接口的方法不应该包含对GUI空间名称的引用(比如AddExplorerBarGroup),因为这会使Presenter知道View太多关于实现方面的细节;
- 尽量让View的方法名称反映Use Case的业务逻辑,这样可以使你的代码具有自表述性并更加易于理解。
二,工作中遇到的问题
1.工作中使用到MVP模式,在我的认识中MVP的写法是下面这样的:
public interface ITestView { event Action SomeEvent; void ShowMessage(string msg); }
public partial class TestView : Window,ITestView { TestPresenter presenter; public TestView() { InitializeComponent(); presenter = new TestPresenter(this); this.btnSomething.Click += (sender, e) => { if (this.SomeEvent != null) SomeEvent(); }; } #region ITestView 成员 public event Action SomeEvent; public void ShowMessage(string msg) { MessageBox.Show(msg); } #endregion }
Presenter构造函数需要一个实现了Iview接口的View,并绑定IView里的事件,进行处理后调用IView里定义的方法
public class TestPresenter { public TestPresenter(ITestView view) { view.SomeEvent += () => { view.ShowMessage("MVP模式的测试"); }; } }
2.但是在工作中我们实际写的MVP模式是下面这种:
IView,只定义View的事件:
public interface ITestView { event Action SomeEvent; }Presenter构造函数需要一个实现了Iview接口的View,并绑定IView里的事件,进行处理后调用自己定义的触发自己事件的方法:View,实现IView,并且在构造函数里初始化Presenter,并且绑定Presenter里的事件 :public partial class TestView : Window,ITestView { TestPresenter presenter; public TestView() { InitializeComponent(); presenter = new TestPresenter(this); presenter.ShowMessage += (msg) => { MessageBox.Show(msg); }; this.btnSomething.Click += (sender, e) => { if (this.SomeEvent != null) SomeEvent(); }; } #region ITestView 成员 public event Action SomeEvent; #endregion }
public class TestPresenter { public event Action<string> ShowMessage; private void OnShowMessage(string msg) { if (ShowMessage != null) ShowMessage(msg); } public TestPresenter(ITestView view) { view.SomeEvent += () => { OnShowMessage("MVP模式测试"); }; } }
三,自己对着两种写法的认识:
第一种方法,IView需要定义用户可能会触发的事件,并且需要提供一些方法。
我的理解是这样的IView相当清楚自己会怎么样,并且还知道自己能干什么,什么自己干不了。编写View的时候只要完成IView里定义的事件,并且把IView里定义的“能干的事情”都实现,不需要去关注Presenter里到底会怎么样。
编写Presenter的人清楚的知道View能干什么事情,会触发什么事件。知道处理一些业务逻辑后自己该怎样去指挥View。
自己感觉这样的方法,主控权是Presenter。View只是去执行,而且必须执行,没有选择权。(因为View继承IView接口,必须实现IView里定义的方法)
第二种方法,IView只定义用户可能会触发的事件。
我的理解是这样的IView只知道自己会怎么样,并不知道自己能干什么。编写View的时候需要要完成IView里定义的事件,并且需要清楚Presenter会发生什么样的事件,我要不要处理Presenter里发生的事件。
编写Presenter的人不知道View能干什么事情,当接受到View发生的事件后,处理一些业务逻辑后,只是触发自己的事件,View层处理不处理,Presenter就不关心了。
自己感觉这样的方法,Presenter 和 View 都回去控制一些东西,View有了选择权。(因为View可以监听Presenter的事件,也可以不监听)。
四,想向大家请教一些问题:
自己老感觉MVP模式应该是第一种写法,但是和我们项目中用到的不一样,而且思想上好像有很大的差别,希望大家能给我一些指导,
第二种方法有什么好处? 真正的MVP模式处理这种问题应该是怎么写的?