MVC、MVP、MVVM 模式(待续)

 

本文将从收集来的资料整理分析MVC模型的各类应用以及其演化历程html

1、 介绍 MV* 模式前端

MVC、MVP、MVVM 模式都是为了解决图形界面应用程序复杂性管理问题而产生的应用架构模式。追根溯源,从最经典的Smalltalk-80 MVC模式开始逐步还原图形界面之下最真实的MV*模式。java

GUI程序所面临的问题

图形界面的应用程序提供给用户可视化的操做界面,这个界面提供给数据和信息。用户输入行为(键盘,鼠标等)会执行一些业务逻辑,可能会致使对应用程序数据的变动,数据的变动天然须要用户界面的同步变动以提供最准确的信息。例如用户对一个电子表格从新排序的操做,应用程序须要响应用户操做,对数据进行排序,而后须要同步到界面上。web

在开发应用程序的时候,以求更好的管理应用程序的复杂性,基于职责分离(Speration of Duties)的思想都会对应用程序进行分层。在开发图形界面应用程序的时候,会把管理用户界面的层次称为View,应用程序的数据为Model(注意这里的Model指的是Domain Model,这个应用程序对须要解决的问题的数据抽象,不包含应用的状态,能够简单理解为对象)。Model层对应用程序的业务逻辑无知,只保存数据结构和提供数据操做的接口。spring

有了View和Model的分层,那么就有了两个问题:数据库

  1. 响应用户操做的业务逻辑(例如排序)的管理。
  2. View如何同步Model的变动。

带着这两个问题开始探索MV*模式,会发现这些模式之间的差别能够概括为对这两个问题处理的方式的不一样。而几乎全部的MV*模式都是经典的Smalltalk-80 MVC的修改版。apache

Smalltalk-80 MVC

历史背景

早在上个世纪70年代,美国的施乐公司(Xerox)的工程师研发了Smalltalk编程语言,而且开始用它编写图形界面的应用程序。而在Smalltalk-80这个版本的时候,一位叫Trygve Reenskaug的工程师设计了MVC图形应用程序的架构模式,极大地下降了图形应用程序的管理难度。而在四人众(GoF)的设计模式当中并无把MVC当作是设计模式,而仅仅是把它当作解决问题的一些类的集合。Smalltalk-80 MVC和GoF描述的MVC是最经典的MVC模式。编程

 

MVC的依赖关系

MVC出了把应用程序分红View、Model层,还额外的加了一个Controller层,它的职责就是专门管理应用程序的业务逻辑。Model、View、Controller三个层次的依赖关系以下:设计模式

Controller和View都依赖Model层,Controller和View能够互相依赖。在一些网上的资料Controller和View之间的依赖关系可能不同,有些是单向依赖,有些是双向依赖,这个其实关系不大,后面会看到它们的依赖关系都是为了把处理用户行为触发的业务逻辑的处理权交给Controller。浏览器

MVC的调用关系

用户的对View操做之后,View捕获到这个操做,会把处理的权利交移给Controller(Pass calls);Controller接着会执行相关的业务逻辑,这些业务逻辑可能须要对Model进行相应的操做;当Model变动了之后,会经过观察者模式(Observer Pattern)通知View;View经过观察者模式收到Model变动的消息之后,会向Model请求最新的数据,而后从新更新界面。以下图:

看似没有什么特别的地方,可是由几个须要特别关注的关键点:

  1. View是把控制权交移给Controller,本身不执行业务逻辑。
  2. Controller执行业务逻辑而且操做Model,但不会直接操做View,能够说它是对View无知的。
  3. View和Model的同步消息是经过观察者模式进行,而同步操做是由View本身请求Model的数据而后对视图进行更新。

须要特别注意的是MVC模式的精髓在于第三点:Model的更新是经过观察者模式告知View的,具体表现形式能够是Pub/Sub或者是触发Events。而网上不少对于MVC的描述都没有强调这一点。经过观察者模式的好处就是:不一样的MVC三角关系可能会有共同的Model,一个MVC三角中的Controller操做了Model之后,两个MVC三角的View都会接受到通知,而后更新本身。保持了依赖同一块Model的不一样View显示数据的实时性和准确性。咱们天天都在用的观察者模式,在几十年前就已经被大神们整合到MVC的架构当中。

这里有一个MVC模式的JavaScript Demo,实现了一个小的TodoList应用程序。经典的Smalltalk-80 MVC不须要任何框架支持就能够实现。目前Web前端框架当中只有一个号称是严格遵循Smalltalk-80 MVC模式的:maria.js。

MVC的优缺点

优势

  1. 把业务逻辑所有分离到Controller中,模块化程度高。当业务逻辑变动的时候,不须要变动View和Model,只须要Controller换成另一个Controller就好了(Swappable Controller)。
  2. 观察者模式能够作到多视图同时更新。

缺点

  1. Controller测试困难。由于视图同步操做是由View本身执行,而View只能在有UI的环境下运行。在没有UI环境下对Controller进行单元测试的时候,Controller业务逻辑的正确性是没法验证的:Controller更新Model的时候,没法对View的更新操做进行断言。
  2. View没法组件化。View是强依赖特定的Model的,若是须要把这个View抽出来做为一个另一个应用程序可复用的组件就困难了。由于不一样程序的的Domain Model是不同的

MVC Model 2

在Web服务端开发的时候也会接触到MVC模式,而这种MVC模式不能严格称为MVC模式。经典的MVC模式只是解决客户端图形界面应用程序的问题,而对服务端无效。服务端的MVC模式又本身特定的名字:MVC Model 2,或者叫JSP Model 2,或者直接就是Model 2 。Model 2客户端服务端的交互模式以下:

服务端接收到来自客户端的请求,服务端经过路由规则把这个请求交由给特定的Controller进行处理,Controller执行相应的业务逻辑,对数据库数据(Model)进行操做,而后用数据去渲染特定的模版,返回给客户端。

由于HTTP协议是单工协议而且是无状态的,服务器没法直接给客户端推送数据。除非客户端再次发起请求,不然服务器端的Model的变动就没法告知客户端。因此能够看到经典的Smalltalk-80 MVC中Model经过观察者模式告知View更新这一环被无情地打破,不能称为严格的MVC。

Model 2模式最先在1998年应用在JSP应用程序当中,JSP Model 1应用管理的混乱诱发了JSP参考了客户端MVC模式,催生了Model 2。

后来这种模式几乎被应用在全部语言的Web开发框架当中。PHP的ThinkPHP,Python的Dijango、Flask,NodeJS的Express,Ruby的RoR,基本都采纳了这种模式。日常所讲的MVC基本是这种服务端的MVC。

MVP

MVP模式有两种:

  1. Passive View
  2. Supervising Controller

而大多数状况下讨论的都是Passive View模式。本文会对PV模式进行较为详细的介绍,而SC模式则简单说起。

历史背景

MVP模式是MVC模式的改良。在上个世纪90年代,IBM旗下的子公司Taligent在用C/C++开发一个叫CommonPoint的图形界面应用系统的时候提出来的。

MVP(Passive View)的依赖关系

MVP模式把MVC模式中的Controller换成了Presenter。MVP层次之间的依赖关系以下:

MVP打破了View原来对于Model的依赖,其他的依赖关系和MVC模式一致。

MVP(Passive View)的调用关系

既然View对Model的依赖被打破了,那View如何同步Model的变动?看看MVP的调用关系:

和MVC模式同样,用户对View的操做都会从View交移给Presenter。Presenter一样的会执行相应的业务逻辑,而且对Model进行相应的操做;而这时候Model也是经过观察者模式把本身变动的消息传递出去,可是是传给Presenter而不是View。Presenter获取到Model变动的消息之后,经过View提供的接口更新界面

关键点:

  1. View再也不负责同步的逻辑,而是由Presenter负责。Presenter中既有业务逻辑也有同步逻辑。
  2. View须要提供操做界面的接口给Presenter进行调用。(关键)

对比在MVC中,Controller是不能操做View的,View也没有提供相应的接口;而在MVP当中,Presenter能够操做View,View须要提供一组对界面操做的接口给Presenter进行调用;Model仍然经过事件广播本身的变动,但由Presenter监听而不是View。

MVP模式,这里也提供一个用JavaScript编写的例子。

MVP(Passive View)的优缺点

优势:

  1. 便于测试。Presenter对View是经过接口进行,在对Presenter进行不依赖UI环境的单元测试的时候。能够经过Mock一个View对象,这个对象只须要实现了View的接口便可。而后依赖注入到Presenter中,单元测试的时候就能够完整的测试Presenter业务逻辑的正确性。这里根据上面的例子给出了Presenter的单元测试样例。
  2. View能够进行组件化。在MVP当中,View不依赖Model。这样就可让View从特定的业务场景中脱离出来,能够说View能够作到对业务逻辑彻底无知。它只须要提供一系列接口提供给上层操做。这样就能够作到高度可复用的View组件。

缺点:

  1. Presenter中除了业务逻辑之外,还有大量的View->Model,Model->View的手动同步逻辑,形成Presenter比较笨重,维护起来��比较困难。

MVP(Supervising Controller)

上面讲的是MVP的Passive View模式,该模式下View很是Passive,它几乎什么都不知道,Presenter让它干什么它就干什么。而Supervising Controller模式中,Presenter会把一部分简单的同步逻辑交给View本身去作,Presenter只负责比较复杂的、高层次的UI操做,因此能够把它当作一个Supervising Controller。

Supervising Controller模式下的依赖和调用关系:

由于Supervising Controller用得比较少,对它的讨论就到这里为止。

MVVM

MVVM能够看做是一种特殊的MVP(Passive View)模式,或者说是对MVP模式的一种改良。

历史背景

MVVM模式最先是微软公司提出,而且了大量使用在.NET的WPF和Sliverlight中。2005年微软工程师John Gossman在本身的博客上首次公布了MVVM模式。

ViewModel

MVVM表明的是Model-View-ViewModel,这里须要解释一下什么是ViewModel。ViewModel的含义就是 "Model of View",视图的模型。它的含义包含了领域模型(Domain Model)和视图的状态(State)。 在图形界面应用程序当中,界面所提供的信息可能不只仅包含应用程序的领域模型。还可能包含一些领域模型不包含的视图状态,例如电子表格程序上须要显示当前排序的状态是顺序的仍是逆序的,而这是Domain Model所不包含的,但也是须要显示的信息。

能够简单把ViewModel理解为页面上所显示内容的数据抽象,和Domain Model不同,ViewModel更适合用来描述View。

MVVM的依赖

MVVM的依赖关系和MVP依赖,只不过是把P换成了VM。

MVVM的调用关系

MVVM的调用关系和MVP同样。可是,在ViewModel当中会有一个叫Binder,或者是Data-binding engine的东西。之前所有由Presenter负责的View和Model之间数据同步操做交由给Binder处理。你只须要在View的模版语法当中,指令式地声明View上的显示的内容是和Model的哪一块数据绑定的。当ViewModel对进行Model更新的时候,Binder会自动把数据更新到View上去,当用户对View进行操做(例如表单输入),Binder也会自动把数据更新到Model上去。这种方式称为:Two-way data-binding,双向数据绑定。能够简单而不恰当地理解为一个模版引擎,可是会根据数据变动实时渲染。

也就是说,MVVM把View和Model的同步逻辑自动化了。之前Presenter负责的View和Model同步再也不手动地进行操做,而是交由框架所提供的Binder进行负责。只须要告诉Binder,View显示的数据对应的是Model哪一部分便可。

这里有一个JavaScript MVVM的例子,由于MVVM须要Binder引擎。因此例子中使用了一个MVVM的库:Vue.js。

MVVM的优缺点

优势

  1. 提升可维护性。解决了MVP大量的手动View和Model同步的问题,提供双向绑定机制。提升了代码的可维护性。
  2. 简化测试。由于同步逻辑是交由Binder作的,View跟着Model同时变动,因此只须要保证Model的正确性,View就正确。大大减小了对View同步更新的测试。

缺点

  1. 过于简单的图形界面不适用,或说牛刀杀鸡。
  2. 对于大型的图形应用程序,视图状态较多,ViewModel的构建和维护的成本都会比较高。
  3. 数据绑定的声明是指令式地写在View的模版当中的,这些内容是没办法去打断点debug的。

总结

能够看到,从MVC->MVP->MVVM,就像一个打怪升级的过程。后者解决了前者遗留的问题,把前者的缺点优化成了优势。一样的Demo功能,代码从最开始的一堆文件,优化成了最后只须要20几行代码就完成。MV*模式之间的区分仍是蛮清晰的,但愿能够给对这些模式理解比较模糊的同窗带来一些参考和思路。

 

2、Web MVC简介

1.一、Web开发中的请求-响应模型:

 

 

在Web世界里,具体步骤以下:

一、  Web浏览器(如IE)发起请求,如访问百度

二、  Web服务器(如Tomcat)接收请求,处理请求(好比用户新增,则将把用户保存一下),最后产生响应(通常为html)。

三、web服务器处理完成后,返回内容给web客户端(通常就是咱们的浏览器),客户端对接收的内容进行处理(如web浏览器将会对接收到的html内容进行渲染以展现给客户)。

 

所以,在Web世界里:

都是Web客户端发起请求,Web服务器接收、处理并产生响应。

 

通常Web服务器是不能主动通知Web客户端更新内容。虽然如今有些技术如服务器推(如Comet)、还有如今的HTML5 websocket能够实现Web服务器主动通知Web客户端。

 

到此咱们了解了在web开发时的请求/响应模型,接下来咱们看一下标准的MVC模型是什么。

 

1.二、标准MVC模型概述

MVC模型:是一种架构型的模式,自己不引入新功能,只是帮助咱们将开发的结构组织的更加合理,使展现与模型分离、流程控制逻辑、业务逻辑调用与展现逻辑分离。如图1-2

 

 

图1-2

首先让咱们了解下MVC(Model-View-Controller)三元组的概念:

Model(模型):数据模型,提供要展现的数据,所以包含数据和行为,能够认为是领域模型或JavaBean组件(包含数据和行为),不过如今通常都分离开来:Value Object(数据) 和 服务层(行为)。也就是模型提供了模型数据查询和模型数据的状态更新等功能,包括数据和业务。

 

View(视图):负责进行模型的展现,通常就是咱们见到的用户界面,客户想看到的东西。

 

Controller(控制器):接收用户请求,委托给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图负责展现。 也就是说控制器作了个调度员的工做,。

 

从图1-1咱们还看到,在标准的MVC中模型能主动推数据给视图进行更新(观察者设计模式,在模型上注册视图,当模型更新时自动更新视图),但在Web开发中模型是没法主动推给视图(没法主动更新用户界面),由于在Web开发是请求-响应模型。

 

那接下来咱们看一下在Web里MVC是什么样子,咱们称其为 Web MVC 来区别标准的MVC。

1.三、Web MVC概述

模型-视图-控制器概念和标准MVC概念同样,请参考1.2,咱们再看一下Web MVC标准架构,如图1-3:

 

如图1-3

 

在Web MVC模式下,模型没法主动推数据给视图,若是用户想要视图更新,须要再发送一次请求(即请求-响应模型)。

 

概念差很少了,咱们接下来了解下Web端开发的发展历程,和使用代码来演示一下Web MVC是如何实现的,还有为何要使用MVC这个模式呢?

 

1.四、Web端开发发展历程

此处咱们只是简单的叙述比较核心的历程,如图1-4

 

 

图1-4

1.4.1、CGI:(Common Gateway Interface)公共网关接口,一种在web服务端使用的脚本技术,使用C或Perl语言编写,用于接收web用户请求并处理,最后动态产生响应给用户,但每次请求将产生一个进程,重量级。

 

1.4.2、Servlet:一种JavaEE web组件技术,是一种在服务器端执行的web组件,用于接收web用户请求并处理,最后动态产生响应给用户。但每次请求只产生一个线程(并且有线程池),轻量级。并且能利用许多JavaEE技术(如JDBC等)。本质就是在java代码里面 输出 html流。但表现逻辑、控制逻辑、业务逻辑调用混杂。如图1-5    

 

图1-5

如图1-5,这种作法是绝对不可取的,控制逻辑、表现代码、业务逻辑对象调用混杂在一块儿,最大的问题是直接在Java代码里面输出Html,这样前端开发人员没法进行页面风格等的设计与修改,即便修改也是很麻烦,所以实际项目这种作法不可取。

 

1.4.3、JSP(Java Server Page):一种在服务器端执行的web组件,是一种运行在标准的HTML页面中嵌入脚本语言(如今只支持Java)的模板页面技术。本质就是在html代码中嵌入java代码。JSP最终仍是会被编译为Servlet,只不过比纯Servlet开发页面更简单、方便。但表现逻辑、控制逻辑、业务逻辑调用仍是混杂。如图1-6

 

 

 

图1-6

 

如图1-6,这种作法也是绝对不可取的,控制逻辑、表现代码、业务逻辑对象调用混杂在一块儿,但比直接在servlet里输出html要好一点,前端开发人员能够进行简单的页面风格等的设计与修改(但若是嵌入的java脚本太多也是很难修改的),所以实际项目这种作法不可取。

 

 

JSP本质仍是Servlet,最终在运行时会生成一个Servlet(如tomcat,将在tomcat\work\Catalina\web应用名\org\apache\jsp下生成),但这种使得写html简单点,但还是控制逻辑、表现代码、业务逻辑对象调用混杂在一块儿。

 

1.4.4、Model1能够认为是JSP的加强版,能够认为是jsp+javabean如图1-7

特色:使用<jsp:useBean>标准动做,自动将请求参数封装为JavaBean组件;还必须使用java脚本执行控制逻辑。

 

 

 

图1-7

此处咱们能够看出,使用<jsp:useBean>标准动做能够简化javabean的获取/建立,及将请求参数封装到javabean,再看一下Model1架构,如图1-8。

 

 

图1-8 Model1架构

Model1架构中,JSP负责控制逻辑、表现逻辑、业务对象(javabean)的调用,只是比纯JSP简化了获取请求参数和封装请求参数。一样是很差的,在项目中应该严禁使用(或最多再demo里使用)。

 

1.4.5、Model2在JavaEE世界里,它能够认为就是Web MVC模型

Model2架构其实能够认为就是咱们所说的Web MVC模型,只是控制器采用Servlet、模型采用JavaBean、视图采用JSP,如图1-9

 

 

图1-9 Model2架构

 

具体代码事例以下:

 

 

 

 

 

从Model2架构能够看出,视图和模型分离了,控制逻辑和展现逻辑分离了。

但咱们也看到严重的缺点:

1.  一、控制器:

1.1.一、控制逻辑可能比较复杂,其实咱们能够按照规约,如请求参数submitFlag=toAdd,咱们其实能够直接调用toAdd方法,来简化控制逻辑;并且每一个模块基本须要一个控制器,形成控制逻辑可能很复杂;

1.1.二、请求参数到模型的封装比较麻烦,若是能交给框架来作这件事情,咱们能够从中获得解放;

1.1.三、选择下一个视图,严重依赖Servlet API,这样很难或基本不可能更换视图;

1.1.四、给视图传输要展现的模型数据,使用Servlet API,更换视图技术也要一块儿更换,很麻烦。

 

1.二、模型:

1.2.一、此处模型使用JavaBean,可能形成JavaBean组件类很庞大,通常如今项目都是采用三层架构,而不采用JavaBean。

 

 

 

1.三、视图

1.3.一、如今被绑定在JSP,很难更换视图,好比Velocity、FreeMarker;好比我要支持Excel、PDF视图等等。

 

1.4.5、服务到工做者:Front Controller + Application Controller + Page Controller + Context

即,前端控制器+应用控制器+页面控制器(也有称其为动做)+上下文,也是Web MVC,只是责任更加明确,详情请参考《核心J2EE设计模式》和《企业应用架构模式》如图1-10:

 

 

图1-10

运行流程以下:

 

 

职责:

Front Controller前端控制器,负责为表现层提供统一访问点,从而避免Model2中出现的重复的控制逻辑(由前端控制器统一回调相应的功能方法,如前边的根据submitFlag=login转调login方法);而且能够为多个请求提供共用的逻辑(如准备上下文等等),将选择具体视图和具体的功能处理(如login里边封装请求参数到模型,并调用业务逻辑对象)分离。

 

Application Controller应用控制器,前端控制器分离选择具体视图和具体的功能处理以后,须要有人来管理,应用控制器就是用来选择具体视图技术(视图的管理)和具体的功能处理(页面控制器/命令对象/动做管理),一种策略设计模式的应用,能够很容易的切换视图/页面控制器,相互不产生影响。

 

Page Controller(Command)页面控制器/动做/处理器:功能处理代码,收集参数、封装参数到模型,转调业务对象处理模型,返回逻辑视图名交给前端控制器(和具体的视图技术解耦),由前端控制器委托给应用控制器选择具体的视图来展现,能够是命令设计模式的实现。页面控制器也被称为处理器或动做。

 

Context上下文,还记得Model2中为视图准备要展现的模型数据吗,咱们直接放在request中(Servlet API相关),有了上下文以后,咱们就能够将相关数据放置在上下文,从而与协议无关(如Servlet API)的访问/设置模型数据,通常经过ThreadLocal模式实现。

 

 

到此,咱们回顾了整个web开发架构的发展历程,可能不一样的web层框架在细节处理方面不一样,但的目的是同样的:

干净的web表现层:

    模型和视图的分离;

控制器中的控制逻辑与功能处理分离(收集并封装参数到模型对象、业务对象调用);

控制器中的视图选择与具体视图技术分离。

轻薄的web表现层:

    作的事情越少越好,薄薄的,不该该包含无关代码;

       只负责收集并组织参数到模型对象,启动业务对象的调用;

       控制器只返回逻辑视图名并由相应的应用控制器来选择具体使用的视图策略;

       尽可能少使用框架特定API,保证容易测试。

 

到此咱们了解Web MVC的发展历程,接下来让咱们了解下Spring MVC究竟是什么、架构及来个HelloWorld了解下具体怎么使用吧。

 

本章具体代码请参考 springmvc-chapter1工程。

 

3、MVC和MVP在app中的对比分析以及实际应用

为了解决逻辑处理和UI视图的松散耦合,MVC和MVP的架构模式在不少App中使用比较普遍。

那什么是MVP呢?它又和咱们经常听到的MVC有什么关系了以及区别呢?

MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负 责显示。做为一种新的模式,MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通讯是经过Presenter (MVC中的Controller)来进行的,全部的交互都发生在Presenter内部,而在MVC中View会从直接Model中读取数据而不是经过 Controller。

在MVC里,View是能够直接访问Model的!从而,View里会包含Model信息,不可避免的还要包括一些业务逻辑。 在MVC模型里,更关注的Model的不变,而同时有多个对Model的不一样显示,及View。因此,在MVC模型里,Model不依赖于View,可是 View是依赖于Model的。不只如此,由于有一些业务逻辑在View里实现了,致使要更改View也是比较困难的,至少那些业务逻辑是没法重用的。

MVP如何解决MVC的问题?

在MVP里,Presenter彻底把Model和View进行了分离,主要的程序逻辑在Presenter里实现。并且,Presenter与具 体的View是没有直接关联的,而是经过定义好的接口进行交互,从而使得在变动View时候能够保持Presenter的不变,即重用! 不只如此,咱们还能够编写测试用的View,模拟用户的各类操做,从而实现对Presenter的测试—而不须要使用自动化的测试工具。 咱们甚至能够在Model和View都没有完成时候,就能够经过编写Mock Object(即实现了Model和View的接口,但没有具体的内容的)来测试Presenter的逻辑。 在MVP里,应用程序的逻辑主要在Presenter来实现,其中的View是很薄的一层。所以就有人提出了Presenter First的设计模式,就是根据User Story来首先设计和开发Presenter。在这个过程当中,View是很简单的,可以把信息显示清楚就能够了。在后面,根据须要再随便更改View, 而对Presenter没有任何的影响了。 若是要实现的UI比较复杂,并且相关的显示逻辑还跟Model有关系,就能够在View和Presenter之间放置一个Adapter。由这个 Adapter来访问Model和View,避免二者之间的关联。而同时,由于Adapter实现了View的接口,从而能够保证与Presenter之 间接口的不变。这样就能够保证View和Presenter之间接口的简洁,又不失去UI的灵活性。 在MVP模式里,View只应该有简单的Set/Get的方法,用户输入和设置界面显示的内容,除此就不该该有更多的内容,毫不允许直接访问Model— 这就是与MVC很大的不一样之处。

MVP的优势:

一、模型与视图彻底分离,咱们能够修改视图而不影响模型;
二、能够更高效地使用模型,由于全部的交互都发生在一个地方——Presenter内部;
三、咱们能够将一个Presenter用于多个视图,而不须要改变Presenter的逻辑。这个特性很是的有用,由于视图的变化老是比模型的变化频繁;
四、若是咱们把逻辑放在Presenter中,那么咱们就能够脱离用户接口来测试这些逻辑(单元测试)。

使用方法

一、创建bean

public class UserBean {

    private String mFirstName; private String mLastName; public UserBean(String firstName, String lastName) { this. mFirstName = firstName; this. mLastName = lastName; } public String getFirstName() { return mFirstName; } public String getLastName() { return mLastName; } }

 

 

二、创建model接口(处理业务逻辑,这里指数据读写)

public interface IUserModel {

    void setID(int id); void setFirstName(String firstName); void setLastName(String lastName); int getID(); UserBean load(int id);// 经过id读取user信息,返回一个UserBean  }

 

 

三、创建view接口(更新ui中的view状态),这里列出须要操做当前view的方法

public interface IUserView {

    int getID(); String getFristName(); String getLastName(); void setFirstName(String firstName); void setLastName(String lastName); }

 

 

四、创建presenter(主导器,经过iView和iModel接口操做model和view),activity能够把全部逻辑给presenter处理,这样java逻辑就从手机的activity中分离出来

public class UserPresenter {

    private IUserView mUserView; private IUserModel mUserModel; public UserPresenter(IUserView view) { mUserView = view; mUserModel = new UserModel(); } public void saveUser( int id, String firstName, String lastName) { mUserModel.setID(id); mUserModel.setFirstName(firstName); mUserModel.setLastName(lastName); } public void loadUser( int id) { UserBean user = mUserModel.load(id); mUserView.setFirstName(user.getFirstName()); // 经过调用IUserView的方法来更新显示  mUserView.setLastName(user.getLastName()); } }

 

 

结束语

MVP主要解决就是把逻辑层抽出来成P层,要是遇到需求逻辑上的更改就能够只须要修改P层了或者遇到逻辑上的大概咱们能够直接从写一个P也能够,很 多开发人员把全部的东西都写在了Activity/Fragment里面这样一来遇到频繁改需求或者逻辑愈来愈复杂的时候,Activity /Fragment里面就会出现过多的混杂逻辑致使出错,因此MVP模式对于APP来对控制逻辑和UI的解耦来讲是一个不错的选择!

 

4、MVP模式

 

一个简单的发送内容的demo 先上效果图

 




 

MVP模式的结构

 

 

 

 

一、什么是MVP模式?

 

    分离view 与 model 层的功能,使得程序更加清晰 提升分离 解耦性,减轻activity的工做量 将业务代码单独抽取出来,各自负责各自层应该作的事情。简单一句话说就是基于MVC模式演变而来,提升代码结构清晰度,提升程序解耦性可维护性的模式。

 

    接下来咱们具体梳理下MVP到底是什么

 

    M 对应的Model 数据层,负责操做、获取数据

 

    V 对应的是View 也就是Activity层,负责UI、与用户进行交互

 

    P Presenter 就是咱们要分离的业务逻辑层,链接view和model层,处理业务逻辑。

 

 

 

二、为何要用MVP模式?

 

    Android开发不一样于JavaWeb开发的是利用MVC模式 使得控制器和展现层彻底分离,可是Android中activity有控制器也有view作的事情,若是把布局文件单独分红view层,几乎就没有作什么事情,而activity还承担了部分view层的工做,到最后需求愈来愈多,activity也会越来也胖。因此在Android开发中可使用MVP模式来清晰的分离各自层的工做。一样也利于单元测试。

 

 

 

三、MVP模式怎么用?

 

    view层专门负责UI及用户交互

 

    Persenter层负责全部的业务逻辑的处理

 

    Model层负责数据的获取、操纵

 

    各个层单独分离,用层与层之间用接口交互

 

下面咱们来看一个简单的Demo

 

    Acitivity  也就是view层 实现了ViewInterface接口,分离业务逻辑

 

public class MainActivity extends AppCompatActivity implements ViewInterface, View.OnClickListener {


private EditText content; private EditText author; private Button send; private Button clean; private ProgressBar loading; private PresenterImpl presenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } public void initView(){ content = (EditText) findViewById(R.id.content); author = (EditText) findViewById(R.id.author); loading = (ProgressBar) findViewById(R.id.loading); send = (Button) findViewById(R.id.send); clean = (Button) findViewById(R.id.clean); send.setOnClickListener(this); clean.setOnClickListener(this); presenter = new PresenterImpl(this); } /** * 显示和隐藏进度条 */ @Override public void showProgressBar() { loading.setVisibility(View.VISIBLE); } @Override public void hideProgressBar() { loading.setVisibility(View.GONE); } /** * 发送是成功仍是失败各自须要作的事情 */ @Override public void sendSuccess() { Toast.makeText(this,"发送成功!",Toast.LENGTH_SHORT).show(); } @Override public void sendFailed() { Toast.makeText(this,"发送失败!",Toast.LENGTH_SHORT).show(); } /** * 清除发送内容 */ @Override public void cleanContent() { content.setText(""); } @Override public void cleanAuthor() { author.setText(""); } /** * 获取内容和做者 */ @Override public String getContent() { return content.getText().toString().trim(); } @Override public String getAuthor() { return author.getText().toString().trim(); } /** * Called when a view has been clicked. * @param v The view that was clicked. */ @Override public void onClick(View v) { switch( v.getId()){ case R.id.send: presenter.send(); break; case R.id.clean: presenter.clean(); break; } } @Override protected void onDestroy() { presenter.destroy(); super.onDestroy(); } }

 


ViewInterface

 

public class MainActivity extends AppCompatActivity implements ViewInterface, View.OnClickListener {


private EditText content; private EditText author; private Button send; private Button clean; private ProgressBar loading; private PresenterImpl presenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } public void initView(){ content = (EditText) findViewById(R.id.content); author = (EditText) findViewById(R.id.author); loading = (ProgressBar) findViewById(R.id.loading); send = (Button) findViewById(R.id.send); clean = (Button) findViewById(R.id.clean); send.setOnClickListener(this); clean.setOnClickListener(this); presenter = new PresenterImpl(this); } /** * 显示和隐藏进度条 */ @Override public void showProgressBar() { loading.setVisibility(View.VISIBLE); } @Override public void hideProgressBar() { loading.setVisibility(View.GONE); } /** * 发送是成功仍是失败各自须要作的事情 */ @Override public void sendSuccess() { Toast.makeText(this,"发送成功!",Toast.LENGTH_SHORT).show(); } @Override public void sendFailed() { Toast.makeText(this,"发送失败!",Toast.LENGTH_SHORT).show(); } /** * 清除发送内容 */ @Override public void cleanContent() { content.setText(""); } @Override public void cleanAuthor() { author.setText(""); } /** * 获取内容和做者 */ @Override public String getContent() { return content.getText().toString().trim(); } @Override public String getAuthor() { return author.getText().toString().trim(); } /** * Called when a view has been clicked. * @param v The view that was clicked. */ @Override public void onClick(View v) { switch( v.getId()){ case R.id.send: presenter.send(); break; case R.id.clean: presenter.clean(); break; } } @Override protected void onDestroy() { presenter.destroy(); super.onDestroy(); } }

 

  

 

业务逻辑层

 

*
* 控制 业务层,处理全部的业务逻辑,链接view 和 model
*/

public interface PresenterInterface { public void send(); public void destroy(); public void clean(); }

 

 
从view层获取数据 ,作完相应的业务处理,把事情交给model校验数据,返回结果,根据结果控制view作出相应的UI处理

 

public class PresenterImpl implements PresenterInterface {


ViewInterface viewInterface;
ModelImpl model;
private Handler mHandler = new Handler(); public PresenterImpl() { } public PresenterImpl(ViewInterface viewInterface) { this.viewInterface = viewInterface; model = new ModelImpl(); } @Override public void send() { //显示进度条 viewInterface.showProgressBar(); model.send(viewInterface.getContent(), viewInterface.getAuthor(), new ModelInterface.OnSendLinterer() { @Override public void sendSuccess() { mHandler.post(new Runnable() { @Override public void run() { viewInterface.sendSuccess(); viewInterface.hideProgressBar(); } }); } @Override public void sendFailed() { mHandler.post(new Runnable() { @Override public void run() { viewInterface.sendFailed(); viewInterface.hideProgressBar(); } }); } }); } @Override public void destroy() { viewInterface = null; model.destory(); model = null; } @Override public void clean() { viewInterface.cleanAuthor(); viewInterface.cleanContent(); } }

 

数据层

 

/*
* 数据层,作的事情就是为Presenter层提供数据
*/

public interface ModelInterface {


interface OnSendLinterer{ void sendSuccess(); void sendFailed(); } public void send(String content, String author, OnSendLinterer onSendLinterer); }

 

发送数据请求发送内容是否成功,这里用的是模拟
public class ModelImpl implements ModelInterface {

private ExecutorService executorService ; public ModelImpl(){ //获取一个线程池 根据 虚拟机可用的处理器的最大数量 +1 定义线程池大小 executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()+1); } @Override public void send(String content, String author,final OnSendLinterer onSendLinterer) { executorService.execute(new Runnable() { /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see Thread#run() */ @Override public void run() { //模拟网络请求的耗时操做 try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } //随机boolean 模拟发送成功或失败 Random random = new Random(); if( random.nextBoolean() ){ onSendLinterer.sendSuccess(); }else{ onSendLinterer.sendFailed(); } } }); //再也不接受新的任务 // executorService.shutdown(); /*try { future.get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); }*/ } public void destory(){ executorService = null; } }

 

 

 
项目的结构

 

 

 

 

 

四、MVP模式何时用?
    可能有朋友会以为MVP是为了模式而模式,开发中类变得多了,感受还更加复杂了。咱们知道,当需求过多过于复杂的时候,activity的代码量就会愈来愈多,可能一个activity中会有几千行代码,极大的影响了后期的维护和开发的成本。若是用上MVP就能极大限度的将activity中展现 业务处理 数据存取单独分离出来,虽然类文件增长了很多,可是总体来看程序和代码的结构仍是很清晰的。因此,应用层开发的项目我的以为使用MVP都是比较合适的。
使用MVP模式,建议按功能分包,利于后面的维护和开发
相关文章
相关标签/搜索