MVP的两种实现方式
MVP
有两种变体:Passive View
和Supervising Controller
。两者的主要区别就是View
和Model
是否直接交流。
被动视图(Passive View)
View
和Model
不知道彼此。Model
的变化要告知Presenter
,再由Presenter
来改变View
。
优点是View
和Model
解耦了。缺点是Presenter
会比较重,数据更新需要手动完成,这就意味着放弃了数据绑定功能。
监督控制器(Supervising Controller)
View
可以知道Model
的存在。让Model
通过事件等方式发出数据变化的通知,View
订阅Model
的变化。
这主要是利用了语言或框架的特性让View
直接感知Model
的变化,而不用经过Presenter
,这样可以减少代码量,缺点是View
对Model
耦合了。
M、V、P之间的关系
先看图
为 V 定义一个接口,接口中暴露的方法就是为 P 提供的。这么做的目的是为了让 P 不知道 V 的存在,从而让 P 仅依赖接口,这样更容易测试 P。
V 和 P 是 1:1 的关系。
P 和 M 是 1:N 的关系。
让 View 独立
先看一个典型的View
和Presenter
连接的地方
1 | public class Presenter |
当用户触发一个 UI 行为,比如点击鼠标时,如何让Presenter
响应呢?基本上就两种方式:
- 让
View
直接调用Presenter
中的方法。但这会产生耦合。 View
利用事件等机制实现广播,让Presenter
去订阅View
产生的通知。这样View
就是完全独立的。但需要在释放时取消订阅事件,这比直接调用Presenter
的方法多了一些步骤。
普遍共识是采用第二种方式,让View
不知道Presenter
的存在。但这只是理想情况,现实开发中还有很多复杂性,所以还是得看情况决定用哪种方式。
但是上面的例子View
依然还是知道如何创建Presenter
:
1 | public class View : IView |
此时你有三种选择:
- 在类的外部将
View
和Presenter
连接起来(订阅通知)。 - 创建一个
PresenterFactory
工厂类,构造过程交给它。1
2mPresenter = PresenterFactory.Create<MyPresenter>();
mPresenter.View = this; // 在 seter 中订阅通知 - 使用 IoC 容器注入(推荐)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public interface IHomeView { }
public interface IPresenter<TView> {
TView View { get; set; }
}
public class HomeView : Form, IHomeView
{
private readonly IPresenter<IHomeView> presenter;
public HomeView(IPresenter<IHomeView> presenter) {
this.presenter = presenter;
InitializeComponent();
}
}
至此,View
仅仅只是知道一个什么都没用的IPresenter
接口,从来不会使用它,持有它只是为了保持Presenter
的生命周期。
最后记住在View
关闭时通知Presenter
互相解除引用。
参考
浅谈 MVC、MVP 和 MVVM 架构模式
MVP 和 MVC 是什么以及有什么区别?
如何在 WinForms 中使用 MVP 的依赖注入?