标签:
在Android中使用 MVP 来开发已经出来很久了,刚好Google又出了一系列的architecture samples,在此就整理一下对于MVP的认知和实践总结,这篇文章会随着使用经验的丰富而不断更新。
在没有使用MVP开发之前,我们一直使用的都是MVC模式,其实也不算的MVC,一般我们听到的都是Android中的Activity既是View,又是Controller,即Activity既负责View的显示,又负责处理业务逻辑,这是我们一般听到的,但其实我们的Activity还负责了数据的保存和读取。
当业务逻辑简单,仅仅只是获取数据、展示数据时,我们的代码是这样:
但其实考虑数据的存储、业务逻辑等等,我们是这样:
所有东西都连在一起,太复杂以至于成了一个面了,这个面就是这个God Object:Activity。
想想看,我们是不是把所有东西都写在这个上帝类中了:网络请求、解析Json、处理数据、展示数据、缓存数据等等,全部都在Activity中,尽管有些东西已经封装的很好,但依旧都和Activity扯在一起,基本上所有代码都在Activity中,于是所谓的MVC,就叫做 massive view controller
,超级大的ViewController。
这样做尽管有好处,很容易看清楚业务逻辑,而且写起来很方便、顺手,不知不觉就这样写了。但是很明显,随着需求不断加多,该类代码就越写越多,并且如果需求有改动的话,那改起来很费劲,而且这样写,不敢轻易重构,因为太复杂,动一点可能就会有bug。
那么不想这么做怎么办呢?于是就有了MVP。
典型的MVP就像这样:
很明显:
我们一般把 Activity/Fragment 当做View,那么这时我们的Activity/Fragment中的代码简单多了,响应Presenter的指令对View进行操作,而这些操作是在IVIEW
接口中定义好的。这样大部分代码转移到了Presenter中,而且一些业务代码被封装到了Model层去,这样的代码就会清晰很多,写完一看,哇塞,太清晰了。而且很容易定位bug,毕竟代码职责越清晰越不容易出错。
那么现在分开来介绍:
到这里,关于MVP的基本概念讲解完了,就是分三层写代码,此处建议使用MVP时分包时按照模块分包,模块比较多时,建一个module包,装所有模块,每个模块再细分三层,这样包很清晰、代码也很清晰。
接下来看一些google的示例。
Google samples中的MVP(google MVP示例中这张图真的很完美的阐述了一个简单的app的架构):
Model层获取数据时,使用DataRepository
分发管理,从Presenter处接收指令从RemoteDataSource
或者 LocalDataSource
中获取数据,并回调给Presenter,Presenter再回调给View用于展示。
下面列出一些官方推荐 的点,这些都可以在代码中找到:
Use Fragments as View. The separation between Activity and Fragment fits nicely with this implementation of MVP: the Activity is the overall controller that creates and connects views and presenters,which means the Activity is responsible for the creation of fragments and presenters.
使用Fragment作为View,Activity作为全局的Controller把presenter和view绑定起来。
Create a contract
interface defining the view and the presenter,which contains the View interface and the presenter interface. Contracts are interfaces used to define the connection between views and presenters. This is a amazing concept.
创建 IContract
父类接口来管理同一个模块下的 IView
和 IPresenter
接口,这样很清楚的能看清楚两者的逻辑。
In general, the business logic lives in the presenter and relies on the view to do the Android UI work.
业务逻辑全部在Presenter层,依靠View层来触发。
The view contains almost no logic: it converts the presenter’s commands to UI actions and listens to user actions, which are passed to the presenter.
View几乎没有任何逻辑,响应Presenter层的指令,并接收用户操作传给Prenster。当然很少很轻量的逻辑应该在这一层中处理(判空处理等),这一点应该要自己衡量。View应该是被动的。
我们应该很容易看到在MVP中仅仅有一些基本原则,并没有一个固定的方式去实现MVP,都针对自家的业务需求、逻辑去更好的更合适的使用MVP,只要View和Model分离,代码清晰,易于调试,易于测试,都OK。
MVP的一些指导性原则来约束实现:
知道了MVP是一个方法论,下面说说MVP的另一个实现方式。
MVP另一个实现方式(变种)就是不像一般的把Activity/Fragment作为View,而是把他们作为Presenter来使用,单独把View用一个类来管理。
使用该种方式可以大大减少类的个数,因为是依靠 泛型 来解耦的,所以可以不用提供接口,当然提供了也行,我在实现过程中删除了接口,同时把点击事件的响应结果当成逻辑,放在Presenter中,这样就不用传来传去的。(之前使用Fragment作为View时,点击ListItem 需要修改已读状态,传入到Presenter中修改,Presenter修改完后又调用View.showDetailUI()方法又传回到View中,使用变种后这块就大大减轻工作量了)
使用这种方式个人认为有点怪异,一是使用泛型加大了理解难度,写着写着就不知道怎么调用到这块了;二是删除接口后感觉复用性降低了,逻辑也不能复用、View也不能复用,当然可以不删接口,但是不删接口和原来的MVP有什么区别呢。恩,不过这点可能随着使用MVP越深入会越能理解,Activity到底是View还是控制器,可能随着项目的偏重不同,会适合不同的情况吧。
那么说完了MVP变种,下面总结一下个人使用MVP的心得。
使用MVP开发,顿时感觉清爽很多,虽然多了很多代码,但是瑕不掩瑜,让代码清爽很多。真的很棒,看了重构后的代码,感觉自己之前真是活在噩梦里一样,终于不用把所有代码写在Activity里或是Fragment里了。下面就具体说说使用中的心得体会。
首先是方法论上:
首先分清除什么是 分发业务逻辑、业务逻辑、视图逻辑。
理解这三点对于设计接口、使用MVP写项目、重构等有很大的帮助。视图逻辑可以更好的写View,分发业务逻辑用于Presenter,业务逻辑用于Model,当然一些业务逻辑是在Presenter中的。
然后看什么是View,什么是逻辑,哪些逻辑展示哪些View,哪些操作调用哪些逻辑,这会很好的帮助我们理清代码逻辑、方便相互调用。
一个 View 可以有多个 Presenter,要用到什么业务就加入什么 Presenter,并且实现这个 Presenter 所需要的 View 接口即可,这就是简单的复用逻辑,即Presenter是可复用的。
然后是代码上:
IView
接口里的方法全部是UI相关,show()、hide()等,不应该出现逻辑相关。IPresenter
接口里的方法应该全部是逻辑相关,进行分发,很明显,要持有Model引用,方便对数据进行操作。不应该出现与Model相关的方法,比如LoadFromLocal,LoadFromRemote,这应该是DataRepository处理的事情。Presenter里面只需要告诉Model说我需要这个数据,即LoadData,而不用管从哪Load数据。这里面只需要负责 分发!!!Model
中,对数据进行处理,所有的数据从这里进出,并对数据进行处理。一般创建DataRepository
类对LocalDataSource
,RemoteDataSource
进行管理分发,这三个类都实现IDataSource
接口,保证逻辑一致。最后一些个人体会:
在MVP里面,越来越感到依赖注入的重要性。全部是依赖,全部需要注入,感觉依赖注入框架 Dagger2
是时候展现它强大的功能了。
回调太多。获取数据时,从Presenter中开始callback,再到DataRepository中,再到具体的LocalDataSource或RemoteDataSource的callback,总共回调了三层,增长了链条。很明显,解决这个问题需要用到 RxJava
了,可以省去很多代码,变得简洁是必然的。
Google samples 中的 IContract
接口管理类真的非常棒,管理了IPresenter和IView,如果可以的话,应该让它也管理IDataSource
这个接口,非常清晰的一种方式。
尽管MVP实现了一定的分离,但相当于大量代码从Activity中转移到Presenter和Model中,尽管使用接口使代码清晰很多,但避免不了难看和难以查找,随着业务的不断复杂,代码依旧会越来越多。所以使用clean架构应该是最好。
最后不得不说确实方法数、类都增加了好多。但这也是必然的。解耦、方便测试。
说了那么说,show me the code.
那么,自己写的代码全在GitHub上,全部以分支形式展现:
最基础的分支,全部代码在Activity中: branch_basic
MVP分支,使用MVP重构代码:branch_mvp
MVP另一种实现方式(变种)分支:branch_mvp_variant
之后,肯定会尝试clean架构再次重构。不过由于项目本身较小,所以当个练手的真的不错。
参考:
介绍MVP,非常好的文章
Android MVP模式 简单易懂的介绍方式
Android中的MVP
Introduction to Model View Presenter on Android
MVP变种:
用MVP架构开发Android应用
Android MVP?—?An Alternate ApproachMVP方法论,具有哲学意味的一篇文章,看完后会觉得原来代码还能这么哲学:
说说Android的MVP模式
标签:
原文地址:http://blog.csdn.net/u014099894/article/details/51388170