最近因为感受和同事之间对MVP的理解有所差别,我从新审视了一下MVP的写法,对目前项目中出现的一些问题进行了思考,创造出一种MVP的变形,我暂且称它为MCVP。文末有demo。java
对基础内容不感兴趣的同窗能够直接跳到MCVP的部分。git
MVP是为了解决传统Android开发架构MVC引发的问题的解决方案之一:Activity在MVC架构中既担当了View又充当了Controller的角色,最后成为上帝类而且类代码量爆炸,即便单独分离出Controller类,状况仍然没有获得多大的改变,只是换了一个上帝而已;这时候MVP被推荐出来,它听从依赖倒置原则解耦了原来意义上的Activity(View)和Controller(Presenter),经过接口抽象行为使咱们的UI和业务逻辑分离并得以复用。github
这是一张随处可见的MVP模式图,重申一下MVP的基本概念:数据库
View通常指Activity和Fragment,它们承载用户的UI界面,和用户直接交互。服务器
Model指数据源,它是负责获取和保存数据的对象的集合,而不是特指某一个实体或者叫model的接口,用层的概念来描述它更加合适--Model层。markdown
Presenter是View和Model之间的协调者,负责处理业务逻辑、承接业务逻辑。网络
View和Presenter之间使用接口调用进行解耦,接口上的方法应该是它们具体行为的抽象:做为调用者去调用对方的方法,它会有一个意图,是 i want to do;而被调用者的暴露方法,就是what i can do的集合。至于对方内部是到底怎么实现的,调用者丝绝不须要关心。 而Model是否应该使用接口进行抽象呢?我以为大部分应用都没这个必要,Model接口的维护成本比较高,带来的价值也不大,除非你的数据源真的有多个实现,例如在不一样状况下使用不一样的服务器数据库,能够考虑用接口解耦。架构
MVP模式大体分为两种写法,被动视图(Passive View)和监督控制器(Supervising Controller)。被动视图中View把逻辑和与Model的交互全权交给Presenter,甚至本身的表现也由presenter控制,什么都不须要管;监督控制器中Presenter只是一个逻辑辅助的职能,View会部分接触修改到Model的数据,可是我认为这是被动视图也没法避免的,例如列表显示总要依赖实体类,因此我也更倾向于监督控制器写法。框架
以上不过是我我的理解,欢迎互相讨论。异步
不少小伙伴对MVP甚至是对面向对象的基本原则理解得不够,因而出现不少奇奇怪怪的问题,包括但不限于如下问题:
和第2点同样的问题,没有对View和Presenter的行为进行抽象,Presenter过多参与控制View的显示,违反单一职责原则,甚至出现对某些个View控件进行直接操做的状况。接口只暴露行为的抽象,具体的操做细节不该该暴露出来。
View的所有点击事件使用某种惟一参数传达给Presenter的某个特定方法,在Presenter对参数集中进行判断执行。出现这种状况也是没有对行为进行抽象。View犹如行尸走肉,彻底没有i want to do的意图,就像是把石头丢入黑洞,至于黑洞里面发生了什么事、会发生什么事?I don't know。好好想想,View-UI做为与用户交互的平台,用户从UI点击某个图标的时候,用户到底有没有意图?那么UI的事件应不该该有意图?
上面说了不少奇怪可是不必定会在你项目里面出现了问题,其实它们不是重点,接下来这个问题我相信应该有出现过在你项目里,或者你看过的项目里。
场景与需求:提交订单的页面,提交订单以后会提示能够升级你的订单,升级后可以得到更好的售后服务,但要收取少许服务费,而这个订单是否可以升级,是由后台动态配置。
流程如图所示:
代码大概是这样的(伪代码不用太纠结细节,咱们注意看思路便可):
public interface SubmitOrderContract { interface IView { void askUserForUpgrade(CallBack<Boolean> callback); void onSubmitSucceed(); void onSubmitFailed(); } interface IPresenter { /** * 提交订单 */ void submitOrder(SubmitOrderEntity order); } } 复制代码
public class SubmitOrderActivity extends AppCompatActivity implements SubmitOrderContract.IView { ... @Override public void askUserForUpgrade(final CallBack<Boolean> callback) { final InquiryDialog dialog = new InquiryDialog(this); dialog.setOnResultListener(new InquiryDialog.OnResultListener({ @Override public void onResult(boolean result) { dialog.dismiss(); callback.onResult(result); } }); dialog.show(); } ... } 复制代码
public class SubmitOrderPresenter implements SubmitOrderContract.IPresenter { ... @Override public void submitOrder(final SubmitOrderEntity order) { mApi.submitOrder(order, new HttpApi.HttpListener<SubmitResultEntity>() { @Override public void onCallBackOk(SubmitResultEntity result) { if (result.canUpDate) { mView.askUserForUpgrade(new CallBack<Boolean>() { @Override public void onResult(Boolean p) { submitOnUpgrade(p, order); } }); } else { mView.onSubmitSucceed(); } } }); } private void submitOnUpgrade(boolean isContinue, SubmitOrderEntity order) { if (!isContinue) { mView.onSubmitFailed(); return; } mApi.submitOrderForUpgrade(order, new HttpApi.HttpListener<SubmitResultEntity>() { @Override public void onCallBackOk(SubmitResultEntity submitResultEntity) { mView.onSubmitSucceed(); } }); }} 复制代码
这发生了两个问题:
针对这种特殊场景,我给出的解决方案是:
MCVP MCVP MCVP MCVP MCVP
即 Model-CallbackView-Presenter。 看到CallbackView这个词,聪明的同窗可能已经猜出了答案,是的,意思是一个会回应的View。MCVP是MVP的一个特殊变体,能够随时运用到你的项目里。
在Presenter这个角色的视角中,询问用户是否升级订单,不过是一个异步获取数据的过程而已,咱们彻底能够把View当作是一个数据源,仿照网络请求的方式等待用户选择结果返回。
View只须要询问用户是否确认而后告诉Presenter就足够了,甚至不须要知道Presenter要它询问用户确认些什么东西。就像菜贩子和顾客的关系,菜贩只要知道顾客须要什么蔬菜,而不须要知道顾客买这个瓜回去干些什么,彻底的解耦关系。
当咱们选择这种方案,传递给View的入参变成了带有泛型的回调接口Callback,View只知道咱们须要它回答什么(泛型T),它经过Callback接口回调过去便可。
MCVP彻底避免了业务逻辑的断裂,保持了流程的连贯,presenter对于逻辑流程彻底可知,提 高了逻辑可读性,类中的业务逻辑方法内聚性更加高,你可爱的业务逻辑终于不须要再在View和Presenter的实现上跳来跳去了,刚接手代码的人也简单清楚逻辑流的走向,维护性也提升了;并且彻底符合被动视图的写法,View也不会所以接触到任何实体类。解决了上面所述的两个问题。
看一下MCVP的代码:
public interface SubmitOrderContract { interface IView { void askUserForUpgrade(CallBack<Boolean> callback); void onSubmitSucceed(); void onSubmitFailed(); } interface IPresenter { /** * 提交订单 */ void submitOrder(SubmitOrderEntity order); } } 复制代码
public class SubmitOrderActivity extends AppCompatActivity implements SubmitOrderContract.IView { ... @Override public void askUserForUpgrade(final CallBack<Boolean> callback) { final InquiryDialog dialog = new InquiryDialog(this); dialog.setOnResultListener(new InquiryDialog.OnResultListener({ @Override public void onResult(boolean result) { dialog.dismiss(); callback.onResult(result); } }); dialog.show(); } ... } 复制代码
public class SubmitOrderPresenter implements SubmitOrderContract.IPresenter { ... @Override public void submitOrder(final SubmitOrderEntity order) { mApi.submitOrder(order, new HttpApi.HttpListener<SubmitResultEntity>() { @Override public void onCallBackOk(SubmitResultEntity result) { if (result.canUpDate) { mView.askUserForUpgrade(new CallBack<Boolean>() { @Override public void onResult(Boolean p) { submitOnUpgrade(p, order); } }); } else {mView.onSubmitSucceed(); } } }); } private void submitOnUpgrade(boolean isContinue, SubmitOrderEntity order) { if (!isContinue) { mView.onSubmitFailed(); return; } mApi.submitOrderForUpgrade(order, new HttpApi.HttpListener<SubmitResultEntity>() { @Override public void onCallBackOk(SubmitResultEntity submitResultEntity) { mView.onSubmitSucceed(); } }); } } 复制代码
能够看到,全部业务流程都在submitOrder()中,没有跳出这个方法,submitOrder()很是忠实地完成了它提交订单的任务,没有再假手于人(View)了。
有同窗说出现了回调地狱啊~那很好解决,相信你们都知道解决方法,Rxjava、协程...它们均可以解决。因为篇幅缘由,RxJava代码的部分能够看文末传送门看demo。
MCVP有它的优势,但我也认可它也有本身的缺点:
MCVP只不过是CallbackView与MVP的结合,这个思想彻底能够移植到其余模式上。除了个人举例,它应该会有更多写法,例如从View方法返回Callback而不是Presenter提供Callback,我也会继续继续思考优化MCVP,期待它可以在更多场景发挥做用,使咱们的代码结构变得更加好。
谢谢你的观看!
周末看比卡超去吧~!