JavaShuo
栏目
标签
应用MVP模式对遗留代码进行重构
时间 2019-11-12
标签
应用
mvp
模式
遗留
代码
进行
繁體版
原文
原文链接
AV(Autonomous View)自治视图
在面向终端用户的应用中,都须要一个可视化的UI来与用户交互.这个UI称为View视图.
在早期,咱们习惯将全部前台的逻辑,与视图揉在一块儿,称为AV自治视图.
这些逻辑包括:数据呈现(Display),用户动做的扑捉与响应,数据存储等.
在.Net的Winform和ASP.NET Web Form中,采用的都是事件驱动模型.
AV是将全部UI相关的逻辑都注册到视图自己,或者视图元素对应的事件上.
人机交互应用的3个关注点.
数据在UI上的展现.
UI处理逻辑.
业务逻辑.
AV的缺陷
首先,业务逻辑与UI无关,因此应该最大程度地被重用.而在AV中,业务逻辑糅合在UI中,没法重用.例如,从winform迁移到web form上.
稳定性:业务逻辑>UI处理逻辑>UI.
而3者糅合在一块儿后,具备最弱稳定性的UI决定了总体的稳定性.
这属于典型的"短板效应".
任何涉及到UI的组件都是不可测试性的(至少是很难测试).因此AV对测试不友好.
MVC模式
针对AV的缺陷,采用SOC(关注点分离)来剥离3个部分.
将人机交互应用分为3个部分
Model:对应用状态和业务功能的封装.
维护着整个应用的状态(数据和行为),并实现了全部的业务逻辑,能够看作为一个领域模型.
View:实现可视化界面的呈现,捕捉最终用户的交互操做(键盘,鼠标).
Controller.
View捕获到用户交互操做后会直接转发给Controller,后者完成相应的UI逻辑。
若是须要涉及业务功能的调用,Controller会直接调用Model。
在完成UI处理以后,Controller会根据须要控制原View或者建立新的View对用户交互操做予以响应.
View和Model存在直接的联系.
View能够直接调用Model查询其状态信息。
当Model状态发生改变的时候,它也能够直接通知View.
Model对View的数据状态改变通知,View对Controller的用户交互通知.都是单向的消息交换.
可使用事件机制来实现这两种通知.
也能够经过观察者模式经过注册/订阅的方式来实现.
View做为Model的观察者,经过注册相应的事件来检测数据状态的改变.
Controller做为View的观察者,经过注册相应的事件来处理用户的交互操做.
MVP模式
MVC模式存在的问题
View和Model能够绕过Controller来直接进行交互.
对于用户驱动的程序(人机交互),咱们不须要Model来主动通知View数据状态的变化.因此,Model应该是彻底独立的.
MVP模式的目标
测试(Unit Test)友好.
关注点分离.
正交性.
每个操做都只改变一件事情,而没有其它的反作用.
解依赖
对View和Model解耦.
下降了Presenter对View的依赖.从依赖于具体的View到依赖于抽象的IView接口.
交互
Presenter对Model的单向调用.
Presenter和View之间的双向交互.这个是核心.
Presenter和View之间交互的方式
PV(Passive View)
为了避免作对UI的测试(难到几乎不能),应该在UI中不进行UI逻辑的处理.
一个被动的View.View中的UI元素(控件)不是由View自己操做,而是由Presenter控制对UI元素的操做.
须要将View中的元素以属性或者其余方式暴露,以供Presenter操做.
在数据绑定中,控件类型的选择应该是View内部的逻辑,不该该出如今Presenter中.
因此,在IView的定义中,不能涉及到具体的控件类型.
而是返回一种数据绑定所需的数据类型.
而后在View内部处理数据到控件的绑定.
PV对测试友好,由于全部的UI处理逻辑都在Presenter中,便于测试.
缺陷
对于一个复杂的UI(含有不少元素),IView接口将会十分庞大.
Presenter须要对UI元素进行操做,因此要了解不少的UI细节.形成简单事情复杂化.
Soc
将诸如格式化,数据绑定这些简单的UI逻辑移到View中.在View中进行一些简单的UI逻辑处理.
View自己仅实现单纯独立的UI逻辑,它处理的数据应该是Presenter推送给它的.
因此View尽量不维护数据状态.在Iview接口的定义中不包含属性.
Presenter所需的View状态应该是View在请求交互处理时给它的.
第一次改造:最薄的View.
起源:因为View持有对Presenter的引用,因此理论上,View是能够无限制地调用Presenter的.
基于之前AV的编码习惯,极可能形成如下的问题:
大部分(甚至全部)的UI处理逻辑都写到View中.
而Presenter的做用就是Proxy,仅仅是调用View中的方法而已.
采用事件订阅的方式来完成Presenter和View的交互.
首先,在IView中定义事件Handler.
为了隔离事件参数中e的类型污染(一些控件的事件参数,会引入一些测试不友好的类型),定义一系列的事件参数类型.
而后,在View的控件事件处理函数中.
将处理事件须要的上下文信息,包装到一个自定义的事件参数中,而后 Raise Event.
最后,在Presenter中,订阅IView暴露的各类事件,并进行处理.处理时须要的上下文在自定义的事件参数中.
优缺点
View只完成了纯粹的布局展现.
在事件处理流程中,若是须要Cancel处理,会比较难作到.
第二次的改造
在View中调用Presenter的方法.完成部分的UI逻辑.
工程划分(使用Company来替代真实信息).
Company.MVP.ICommonView.
包含了对使用到的控件的抽象View接口,在每一个接口中暴露出来Presenter须要使用到的属性和函数.
每一种控件类型一个接口.
Company.MVP.ComonViews.
对于每个控件,实现一个继承了IXXXView接口的类.
在这些类中,体现了具体控件的属性和方法的细节.
Company.MVP.Common.
该工程含有3个子文件夹.
ModelObjects. Model的一部分,业务模型的抽象类.
Service. Model的另一部分,定义了数据访问接口.
View:定义了UI页面须要实现的接口.
Company.MVP.Presenter.
Presenter的具体实现.
Company.MVP.Service.
数据访问接口的具体实现.
Company.Client.
具体的UI工程.会实现Common中View的UI页面接口.
工程间依赖.
Prensenter仅仅依赖于ICommonView和Common.而跟具体的UI控件类型,具体的UI画面无关.
因此,可使用一个Presenter来对应多个的View展现(Client).
单元测试
针对Presenter.
对于Service和View,因为P中操做的是二者的接口.因此可使用Mock来模拟这两个部分.
而Model是能够简单地New出来的,不须要进行Mock.
针对Model.
使用业务场景,进行测试.并且对其测试时,不须要进行Mock.
针对View.
能够进行少许的测试.由于有IView接口,因此能够Mock控件的属性和行为,来针对UI页面进行测试.
更换控件类型
UI应用中,最常常遇到的情形.例如,如今要将界面上的一个TextBox控件替换为EditText控件.
在UI实现的Client工程的具体页面类上,将实例化之前的成员时使用的类型从TextBoxView修改成EditTextView便可.
其余的类和工程不须要修改.
改动被限定在了特定的地方.避免了短板效应.
总结
关于代码量
使用MVP模式后,代码量是确定不会比原先的少的.
考虑到View的重用,以及子Presenter的重用.代码量增长的也很少.
关于控件的View类型的接口抽象及实现.
对于控件的View的接口,能够只针对一个页面,也能够在工程前期,定义好对一个控件所需的全部的操做.这样就在全系统中使用一份View的接口.
View接口对外暴露的应该是操做,而不是以控件属性/方法的视角看待.也就是说Prensenter须要对控件进行什么类型的操做,就暴露一个这样的操做出来.
关于控件差别性的问题.
系统中不一样界面中,同一控件的操做接口多是不一样的.
按照MVP的本意,是没有View重用的概念的.
可是,咱们能够将同一控件基本的公用行为抽象为一个接口,而后使用一个类来实现它.而后在有特殊操做接口的画面中,再定义一个继承自公用接口的接口,而后使用一个类继承公用类,并实现该接口.
关于控件的事件链.
在现有的代码中,有不少地方用到了事件链的连锁效应.
我的认为,这是一种不太好的编程方式.这样控件之间相互的依赖关系变得如此的复杂.改动事件链上的任何一个控件的任何一个事件处理,都须要查看其连带的连锁反映.
在MVP中,咱们在处理一个控件的操做时,会把全部控件须要展现的内容一次性地处理好,而后一把交给View进行展现.而不是使用事件的连锁效应.
这样,就解除了控件之间在事件上的相互依赖关系.
关于单元测试.
对于业务系统的单元测试,纯粹的代码覆盖率是没有意义的.
须要关注的是测试的场景覆盖率.
即便覆盖百分百的代码.可是漏测了一种Case,同样会出现Bug.
因此,咱们须要有很清晰的业务逻辑说明,来指导咱们进行单元测试时的Case场景输入.
事件处理流程三部曲
IView中定义Event.
Event ButtonClick.
View中触发事件.
Private withevents _item as button
Public sub itemClick() handles _item.Click
RaiseEvent ButtonClick
Presenter中挂接并处理事件
AddHandler OKButton.ButtonClick , Addressof Save.
目标
一个(种)控件,对外提供统一的行为接口.
行为包括:属性,方法,事件.
画面类职责清晰.
仅包含了控件的集合.
没有任何的逻辑处理代码.
更换控件类型时,改动最小.
仅需更改画面类中New控时使用的实际View类型.
业务代码和控件逻辑的分离.
业务代码放在Model中.
控件逻辑,封装在View的实际实现类中.
Model是彻底独立的,不依赖于任何模块.
相关文章
1.
“遗留代码是**!”
2.
MVP应用架构模式
3.
传统企业遗留系统的重构及代码优化
4.
重构遗留代码(1):金牌大师
5.
【Android】【代码架构】MVP架构研究(二):MVP模式简介
6.
MVP模式应用
7.
使用webStorm进行代码重构
8.
使用AndroidStudio进行代码重构
9.
使用Intellij IDEA 进行代码重构
10.
遗留系统重构的模式与原则
更多相关文章...
•
Eclipse 代码模板
-
Eclipse 教程
•
Markdown 代码
-
Markdown 教程
•
委托模式
•
IntelliJ IDEA代码格式化设置
相关标签/搜索
代码重构
遗留
重构与模式
代理模式
结构模式
架构模式
代码模板
代码架构
零行代码
三行代码
Hibernate教程
Redis教程
MySQL教程
应用
代码格式化
设计模式
0
分享到微博
分享到微信
分享到QQ
每日一句
每一个你不满意的现在,都有一个你没有努力的曾经。
最新文章
1.
eclipse设置粘贴字符串自动转义
2.
android客户端学习-启动模拟器异常Emulator: failed to initialize HAX: Invalid argument
3.
android.view.InflateException: class com.jpardogo.listbuddies.lib.views.ListBuddiesLayout问题
4.
MYSQL8.0数据库恢复 MYSQL8.0ibd数据恢复 MYSQL8.0恢复数据库
5.
你本是一个肉体,是什么驱使你前行【1】
6.
2018.04.30
7.
2018.04.30
8.
你本是一个肉体,是什么驱使你前行【3】
9.
你本是一个肉体,是什么驱使你前行【2】
10.
【资讯】LocalBitcoins达到每周交易比特币的7年低点
本站公众号
欢迎关注本站公众号,获取更多信息
相关文章
1.
“遗留代码是**!”
2.
MVP应用架构模式
3.
传统企业遗留系统的重构及代码优化
4.
重构遗留代码(1):金牌大师
5.
【Android】【代码架构】MVP架构研究(二):MVP模式简介
6.
MVP模式应用
7.
使用webStorm进行代码重构
8.
使用AndroidStudio进行代码重构
9.
使用Intellij IDEA 进行代码重构
10.
遗留系统重构的模式与原则
>>更多相关文章<<