之前有一个关于 MVP 的疑惑:1个 Presenter 能否对应多个 View?,在我的这篇文章中有写过。现在回过头来仔细想想,感觉有点不对劲:1个 Presenter 为什么会有对应多个 View 的需求呢?那篇文章中的使用场景是:
我有 A B C 三个页面,我有一个 Presenter,用来处理一个数据类型的增删改查,但是界面上,A界面只需要查询,B界面只需要删除,C界面只需要增和改。
我们都知道,Presenter 是用来抽离 Activity 或 Fragment 中的业务代码的,何为业务代码?就是这个页面涉及到的业务逻辑。关于数据的增删改查,那是 M 应该做的事,而绝不是 Presenter 该去处理的。 所以,Presenter 应当只对应一个 View,如果有界面复用 Presenter 的情况,那我们得考虑为什么会有这种情况呢?复用 Presenter 代表着业务逻辑是一致的,不同的页面理当有着不同的业务逻辑。当然,这里说的业务是简单,最小颗粒化的业务,如果一个页面十分复杂,Presenter 中集合了大量业务代码,那么在某些小的页面是有可能复用 Presenter 中的部分业务代码的。所以这种情况下,Presenter 会对应多个 View,那么这个场景使用上篇文章中的做法是可以的:复用 Presenter,将 View 抽离,利用继承实现多个界面的定制需求,但不会是上篇文章中的那种使用场景。
为什么会有上篇文章中的场景呢?因为目前项目中的 Presenter 做了 M 该做的事,举个栗子:
1 | public class PrinterPresenter extends AbsPresenter implements IPrinterPresenter, LoaderManager.LoaderCallbacks<Cursor> { |
代码做了精简,表达出意思即可。可以看到,Presenter 实现了 LoaderManager.LoaderCallbacks
1 | public class PrinterProviderHelper extends BaseProviderHelper { |
可以看到,PrinterProviderHelper 才是我们的 M 层,但是它并没有对数据的操作进行封装,仅仅提供了最底层的方法,数据操作的封装反而写到了 Presenter 里,这就是不合理的地方,导致了前面出现的场景:A 界面只需要查询,B 界面只需要删除,C 界面只需要增和改
,而增删改查的接口都封装在了 Presenter 里面,以至于出现需要复用 Presenter 的伪需求。理论上应该由 M 封装数据操作,然后 P 持有 M 的引用,进行方法调用即可。就像这样:
1 | class M { |
如此这般, A B C 3个界面对应各自的 P,对于数据的操作只需要持有 M 进行方法调用就好了,那么关于 View 接口的定制也是一对一的,不会出现多的 do nothing 的方法了。其实这在Google 中的 MVP 架构演示中就已经表达出来了。只不过来到公司项目的中 Presenter 就是这样实现的,加上大多数时候程序员都是以功能优先,很少有时间真正去思考一些东西。至于数据操作涉及的异步线程的切换,使用 CursorLoader、AsyncTask 亦或是 RxJava 都是可以的,这就看项目实际情况了。