应用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对用户交互操作予以响应.
- Model:对应用状态和业务功能的封装.
- 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在请求交互处理时给它的.
- PV(Passive View)
- MVC模式存在的问题
- 第一次改造:最薄的View.
- 起源:由于View持有对Presenter的引用,所以理论上,View是可以无限制地调用Presenter的.
- 基于以前AV的编码习惯,很可能造成以下的问题:
- 大部分(甚至所有)的UI处理逻辑都写到View中.
- 而Presenter的作用就是Proxy,仅仅是调用View中的方法而已.
- 基于以前AV的编码习惯,很可能造成以下的问题:
- 采用事件订阅的方式来完成Presenter和View的交互.
- 首先,在IView中定义事件Handler.
- 为了隔离事件参数中e的类型污染(一些控件的事件参数,会引入一些测试不友好的类型),定义一系列的事件参数类型.
- 然后,在View的控件事件处理函数中.
- 将处理事件需要的上下文信息,包装到一个自定义的事件参数中,然后 Raise Event.
- 最后,在Presenter中,订阅IView暴露的各种事件,并进行处理.处理时需要的上下文在自定义的事件参数中.
- 优缺点
- View只完成了纯粹的布局展示.
- 在事件处理流程中,如果需要Cancel处理,会比较难做到.
- 起源:由于View持有对Presenter的引用,所以理论上,View是可以无限制地调用Presenter的.
- 第二次的改造
- 在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页面接口.
- Company.MVP.ICommonView.
- 工程间依赖.
- Prensenter仅仅依赖于ICommonView和Common.而跟具体的UI控件类型,具体的UI画面无关.
- 所以,可以使用一个Presenter来对应多个的View展示(Client).
- 单元测试
- 针对Presenter.
- 对于Service和View,由于P中操作的是两者的接口.所以可以使用Mock来模拟这两个部分.
- 而Model是可以简单地New出来的,不需要进行Mock.
- 针对Model.
- 使用业务场景,进行测试.而且对其测试时,不需要进行Mock.
- 针对View.
- 可以进行少量的测试.因为有IView接口,所以可以Mock控件的属性和行为,来针对UI页面进行测试.
- 针对Presenter.
- 更换控件类型
- UI应用中,最经常遇到的情形.例如,现在要将界面上的一个TextBox控件替换为EditText控件.
- 在UI实现的Client工程的具体页面类上,将实例化以前的成员时使用的类型从TextBoxView修改为EditTextView即可.
- 其他的类和工程不需要修改.
- 改动被限定在了特定的地方.避免了短板效应.
- UI应用中,最经常遇到的情形.例如,现在要将界面上的一个TextBox控件替换为EditText控件.
- 总结
- 关于代码量
- 使用MVP模式后,代码量是肯定不会比原先的少的.
- 考虑到View的重用,以及子Presenter的重用.代码量增加的也不多.
- 关于控件的View类型的接口抽象及实现.
- 对于控件的View的接口,可以只针对一个页面,也可以在工程前期,定义好对一个控件所需的所有的操作.这样就在全系统中使用一份View的接口.
- View接口对外暴露的应该是操作,而不是以控件属性/方法的视角看待.也就是说Prensenter需要对控件进行什么类型的操作,就暴露一个这样的操作出来.
- 关于控件差异性的问题.
- 系统中不同界面中,同一控件的操作接口可能是不同的.
- 按照MVP的本意,是没有View重用的概念的.
- 但是,我们可以将同一控件基本的公用行为抽象为一个接口,然后使用一个类来实现它.然后在有特殊操作接口的画面中,再定义一个继承自公用接口的接口,然后使用一个类继承公用类,并实现该接口.
- 关于控件的事件链.
- 在现有的代码中,有很多地方用到了事件链的连锁效应.
- 个人认为,这是一种不太好的编程方式.这样控件之间相互的依赖关系变得如此的复杂.改动事件链上的任何一个控件的任何一个事件处理,都需要查看其连带的连锁反映.
- 在MVP中,我们在处理一个控件的操作时,会把所有控件需要展示的内容一次性地处理好,然后一把交给View进行展示.而不是使用事件的连锁效应.
- 这样,就解除了控件之间在事件上的相互依赖关系.
- 关于单元测试.
- 对于业务系统的单元测试,纯粹的代码覆盖率是没有意义的.
- 需要关注的是测试的场景覆盖率.
- 即使覆盖百分百的代码.但是漏测了一种Case,一样会出现Bug.
- 所以,我们需要有很清晰的业务逻辑说明,来指导我们进行单元测试时的Case场景输入.
- 事件处理流程三部曲
- IView中定义Event.
- Event ButtonClick.
- View中触发事件.
- Private withevents _item as buttonPublic sub itemClick() handles _item.ClickRaiseEvent ButtonClick
- Presenter中挂接并处理事件
- AddHandler OKButton.ButtonClick , Addressof Save.
- 目标
- 一个(种)控件,对外提供统一的行为接口.
- 行为包括:属性,方法,事件.
- 画面类职责清晰.
- 仅包含了控件的集合.
- 没有任何的逻辑处理代码.
- 更换控件类型时,改动最小.
- 仅需更改画面类中New控时使用的实际View类型.
- 业务代码和控件逻辑的分离.
- 业务代码放在Model中.
- 控件逻辑,封装在View的实际实现类中.
- Model是完全独立的,不依赖于任何模块.
- 关于代码量
应用MVP模式对遗留代码进行重构的更多相关文章
- Android MVP模式 谷歌官方代码解读
Google官方MVP Sample代码解读 关于Android程序的构架, 当前(2016.10)最流行的模式即为MVP模式, Google官方提供了Sample代码来展示这种模式的用法. Repo ...
- android MVP模式介绍与实战
android MVP模式介绍与实战 描述 MVP模式是什么?MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数 ...
- 从最简单的HelloWorld理解MVP模式
版权声明:本文为博主原创文章,转载请注明出处:http://www.cnblogs.com/joy99/p/6116855.html 大多数编程语言相关的学习书籍,都会以hello,world这个典型 ...
- 使用MVP模式重构代码
之前写了两篇关于MVP模式的文章,主要讲得都是一些概念,这里谈谈自己在Android项目中使用MVP模式的真实感受,并以实例的形式一起尝试来使用MVP模式去重构我们现有的代码. 有兴趣的童鞋可以先去阅 ...
- MVP模式, 开源库mosby的使用及代码分析
Android中的构架模式一直是一个很hot的topic, 近年来Architecture components推出之后, MVVM异军突起, 风头正在逐渐盖过之前的MVP. 其实我觉得MVP还是有好 ...
- Android MVP模式
转自http://segmentfault.com/blogs,转载请注明出处Android MVP Pattern Android MVP模式\[1\]也不是什么新鲜的东西了,我在自己的项目里也普遍 ...
- .Net平台-MVP模式初探(一)
为什么要写这篇文章 笔者当前正在负责研究所中一个项目,这个项目基于.NET平台,初步拟采用C/S部署体系,所以选择了Windows Forms作为其UI.经过几此迭代,我们发现了一个问题:虽然业务逻辑 ...
- Android开发之初识MVP模式
各位亲爱的小伙伴,有没有想我啊,我胡汉wing又回来了. 很长一段时间没有更新博客..原因是..从离职回到学校以后,一直在享受最后的学生时光(打游戏).. 游戏固然很爽,但是觉得实在很荒废,于是半夜诈 ...
- MVP 模式简单易懂的介绍方式
为什么用Android MVP 设计模式? 当项目越来越庞大.复杂,参与的研发人员越来越多的时候,MVP 模式 的优势就充分显示出来了. MVP 模式是 MVC 模式在 Android 上的一种变体, ...
随机推荐
- Arduino LM35温度检测
一. 接线原理图 二.实物图 三.代码例子
- AndroidStudio 内存泄漏的分析过程
前言部分这次泄漏是自己代码写的太随意引起的,讲道理,代码写的太为所欲为了,导致有些问题根本就很难发现. 泄漏产生的原因,由于activity未被回收导致.这里给我们提出的一个警示,在使用上下文的时候, ...
- HTML 1.1页面js修改文字颜色
昨天的报告页面,想要实现根据不同文字内容改变字体颜色,效果图: 调试了半天出不来效果,最后请教了前端,上代码: <!DOCTYPE html> <html lang="en ...
- 启动模拟器的qq
#coding = utf-8from appium import webdriver '''1.手机类型2.版本3.手机的唯一标识 deviceName4.app 包名appPackage5.app ...
- AtCoder Grand Contest 021完整题解
提示:如果公式挂了请多刷新几次,MathJex的公式渲染速度并不是那么理想. 总的来说,还是自己太弱了啊.只做了T1,还WA了两发.今天还有一场CodeForces,晚上0点qwq... 题解还是要好 ...
- Modbus串行通信
一.Modbus通信协议简介 1. Modbus协议 Modbus 是一个请求/应答协议,并且提供功能码规定的服务.Modbu协议是 OSI 模型第 7 层上的应用层报文传输协议. MODBUS协议支 ...
- Oralce导入数据库出现某一列的值太大
这是由于导出的文件所运行的Oracle,和导入所运行的Oracle机器字符集不相同导致的,在UTF-8中有的汉字占三个字节, 并不是所有的都占两个字节,
- Linux思维导图之文件压缩
- 单例模式的理解【php】
单例模式(Singleton Pattern):顾名思义,就是只有一个实例.作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 为什么要使用单例模式 1.P ...
- jupyter记事本的安装和简单应用
1.概述 jupyter记事本是一个基于Web的前端,被分成单个的代码块或单元.根据需要,单元可以单独运行,也可以一次全部运行.这使得我们可以运行某个场景,看到输出结果,然后回到代码,根据输出结果对代 ...