龙空技术网

C#架构-客户端MVP揭秘(标题正确版本)

爱国程序员 417

前言:

当前姐妹们对“mvc按钮单击事件”都比较注意,姐妹们都需要学习一些“mvc按钮单击事件”的相关知识。那么小编同时在网摘上网罗了一些关于“mvc按钮单击事件””的相关文章,希望同学们能喜欢,大家快快来了解一下吧!

我在C#架构-客户端MVP揭秘一文中讲到了我所知道的MVC的历史以及它构建出来的初衷,其中核心就是构建model层和view层的对应关系。数据抽象和显示抽象一一对应起来,操作数据以便操作显示,显示改变也能改变数据,那么两者之间就紧密关联起来了。说到这里大家会不会觉得这个和vuejs或者wpf中的MVVM架构很像,用过vue或者wpf的都知道显示和数据是紧密联系的。

比如说我保存一个单据,我不再需要再次获取界面的数据然后调用业务层了,因为显示数据已经和model层绑定了,那么我直接可以拿model层数据去保存单据就行了。那或许你就有疑问了,那MVVM和MVC有什么区别呢?答案就是MVC view层和model层的关系是手动联系的,需要写代码的,而MVVM中他们的绑定关系交给了框架去做了,就是这么一点区别。这一点上MVC和MVP是一样的,唯一的区别是MVP里面model层不直接和view层绑定关系,而是把这个关系放到了controller层里面去了,也就是model层数据变化先传递到controller层,然后控制层把数据变化转到view层。类似下面这个图所示。

图1

这么做有什么好处呢?就是彻底解放view层,view层和model没有直接的关系了,都是通过控制层进行数据传递,这样view层可以作为一个组件进行复用,不会受到model层的影响。

光说不练假把式,还是通过一个C#例子进行举例,然后主要分析一下MVP和MVC的区别,以及从高内聚,松耦合,可扩展这三个点去分析一下MVP的优点。

图2

请看图2,目的是输入数字,点击显示按钮,label1显示这个数字,点击自增,label1自增显示。例子和C#架构-客户端MVP揭秘这篇文章是一样的,只是实现方式从MVC改成了MVP。

图3

图3是MVP中P层,也就是Presenter层,后续称为"主导器"。主导器和MVC中的Controller一样,属于交互作用,用于处理用户输入交互,消息处理交互等。然后把View层数据传递到Model层建立一对一关系,当View层数据变化也通过主导器传递到View层显示。请看图3代码,

MainPresenter主导器构造函数两个形参,一个是IMainView,一个是IModel。前者是MVP中的View层,后者是MVP里面的M层。

图4

请看图4的IMainView定义,这个接口定义了两个事情一个方法,DisplayValue和IncrementValue两个事件,用于主导器订阅前端的"显示"按钮和"自增"按钮这两个事件,图3可以看出来订阅过程,MainPresenter主导器对DisplayValue这个事件绑定了View_DisplayValue这个方法,其中执行了this.model.SetValue(e.value)和this.model.DisplayValue()这两个方法。

图5

图5 可以看到FrmMain主界面实现了IMainView接口,触发了图4的两个事件。

然后事件就传递到了MainPresenter主导器,执行View_DisplayValue这个方法。

图6

图6是Model层代码,首先SetValue方法是赋值,DisplayValue方式是显示,显示的方式是触发Changed事件,这个事件在哪里订阅呢,在MainPresenter主导器里面订阅,请看图3代码

在MainPresenter主导器构造函数里面,这句this.model.Changed+=Model_Changed

请看Model_Changed方法体,执行了this.view.Display(e.newval);这句代码,请看图5,把数据展示。

同理在看一下点击"自增"按钮的过程,首先触发这个IncrementValue事件,把事件传递到主导器中,主导器订阅了这个事件,执行this.model.Increment();Model层对应的操作无非是value++,然后触发Changed事件,把事件传递到主导器中,然后主导器执行View中的方法

this.view.Display(e.newval);

我们看看是怎么创建主导器的。请看图7

图7

图7代码非常的好明白,无非就是创建FrmMain窗体对象和MvpModel Model对象然后创建主导器对象,把三者关联起来。

这里的代码说实话没有很难看懂的代码,但是和我们平时写的代码有区别,在没有任何设计的情况下,我们总是喜欢按照思维顺序的方式去写代码,也就是三层架构的方式去写,不会单独去写交互层和model层。但是为什么要这么做,这么做的好处有什么,我们开篇说好处是高内聚,松耦合,可扩展,接下来我们就通过这三点总结一下。

我们再看一下MainFrm代码。

图8

View层只实现了IMainView接口,我们暂时不用理会FrmBase这个基类,我们就当是继承了Form。继承了IMainView接口,然后传递触发了事件,以及显示数据,没有其他的交互逻辑了,View层只做展示,不做别的事情,所谓别的事情就是不会去调用Bll层去查数据,也不会写一些交互逻辑,比如说验证数据的准确性,这些都在主导器完成,这样有什么好处呢?好处就是高内聚和松耦合。实现单一职责这个设计原则,也就是高内聚,因为这里就是单单显示代码以及事情的传递,非常清楚,不干别的工作。

通过接口和主导器进行交互的好处就是,如果项目比较大,人员比较多,就可以做界面和做交互的事情分开不同的人做,他们唯一需要共同商讨的就是这个IMainView事件怎么定义就行了。做界面的不需要知道主导器是怎么实现的,怎么定义的,怎么调用,都不需要知道。那么这个View层就变成了用户控件一样,可以匹配不同的主导器,主导器怎么变都可以,只是实现共同定义的IMainFrm接口就行了。

请看图7的代码,完全可以创建多个主导器,比如一个主导器用于处理UI交互,一个主导器处理Hook事件,也就是比如扫码事件,快捷键事件等等,一个主导器处理Mqtt事件交互。

这样就实现了可扩展的好处。因为View层代码很少了,大部分代码转移到了主导器里面去了,那么主导器代码就会变得很多很杂,除了通过分部代码把代码拆分,也可以通过建立多个子主导器进行分解代码,那么项目大了人员多了的时候主导器都可以分给不同的人去做。

伪代码就变得如图9这样。

图9

这里还有一个好处就是Model层,一个Model层可以控制多个视图。我们知道Model层的构建初衷就是为了和View层数据一一对应,显示抽象和数据抽象关联在一起,两者相互影响,那数据保存的时候不需要重新从界面上获取值,而是直接拿Model层的数据去保存就行了,显示数据,不需要拿到控件进行展示,而是直接将Model的值改掉就会同步改掉界面上的数据了。

如果存在不同的界面对应的Model层数据是一样的,我们就可以通过一个Model绑定多个主导器,从而同时控制不同的界面。我们想一想有没有这种场景。比如登入界面的用户名,有没有可能除了登录界面有用,登入之后的主界面也需要用到呢,当然有这种可能性,那么我们可以定义一个Model,绑定多个主导器,从而控制多个界面显示。这里的关键点,为什么可以做到这点,就是因为我们是通过主导器订阅Model事件的方式来触发数据变化展示到界面上的。那么多个主导器订阅一个model事件,当触发一个这个事件,可以传递给多个主导器。这就是为什么Model层要通过触发事件的方式传递出去的原因。

我们再来看看这个图

图10

请看图10,再结合我们上面所讲。这个图说明的很明白,第一点就是View和Model是没有任何关联的,View和Presenter是通过接口方式交互的,这就让View独立提供了条件,看红色的步骤,第1步是通过Presenter层订阅View事件进行事件传递,同时可以做到将Presenter进行拆分,因为是通过接口交互的,第2步是Presenter直接调用Model方法,第3步Model传递数据给Presenter是通过Presenter订阅Model事件方式传递的,这样的好处就是一个Model可以绑定多个Presenter,从而绑定多个界面。第四步是通过接口方式把数据传递给View层进行展示。

MVP和MVC的区别也可以得出了,我们看这篇文章C#架构-客户端MVP揭秘和本文进行对比就知道了,MVC中View层还是会和Model层有耦合,这样导致View无法真正的独立。MVP解决了这个问题。

相信我们上述讲解就明白了MVP为什么这么设计。

接下来我们优化一下代码,让创建Presenter对象的方式更加方便好看一点。

直接发代码

图11

请看图11,FrmMain绑定特性IMainPresenter这个主导器接口。

图12

请看图12,FrmMain 继承了这个FrmBase,其中构造函数执行了

_controller.ControllerBinding(this)

图13

获取界面特性,获取主导器接口,然后实例化这个主导器,这里实例化的过程是用DI的方式。

图14

这样就可以实例化主导器实例。

图13 presenter.BindingView(view);这句话就给主导器绑定了View层。

图15

这样入口只要初始化DI就行了。具体DI里面做了什么,因为还没有单独写DI的文章

,请看文章C#核心-反射揭秘1发射系列文章。

这里这么处理的好处就是Model层需要引用bll,dal层只需要引用接口方式就能使用了。

以上MVP是指Passive View,意思就是View层完全独立开的。

Supervising Controller 这个模式是指model层数据除了通过主导器传到view还可以直接类似MVC的方式让View定义Model事件,但是一般MVP都是使用Passive View,说明Supervising Controller这个实战还是有问题的,这里就不讲诉这个

Supervising Controller了。

MVVM和MVP的区别就在于在MVP Passive View这个基础之上,Model和View的数据交互的过程让框架去做了,我们从上面可以看到我们都是手写方式去做这个过程的。

下一篇文章会通过C#来实战一下MVVM,并说明它的好处

标签: #mvc按钮单击事件