用Kotlin对MVP基本结构实现

之前我有写过一个开源的Java实现的基于MVP架构的开源Android工程基本结构《MVP架构的Android基础框架》

最近在用Kotlin做项目,计划使用MVP架构在开始一个新的项目,来继续聊聊Kotlin中MVP的实现。

参考链接:

《MVC,MVP 和 MVVM 的图示》
《浅谈 MVC、MVP 和 MVVM 架构模式》
《Android MVP 不一样的实现方案》

MVP架构

复杂的软件必须有清晰合理的架构,否则无法开发和维护。MVC(Model-View-Controller)是最常见的软件架构之一,业界有着广泛应用。

MVC中所有通信都是单向的。

  1. View 传送指令到 Controller
  2. Controller 完成业务逻辑后,要求 Model 改变状态
  3. Model 将新的数据发送到 View,用户得到反馈

MVP 模式将 Controller 改名为 Presenter,同时改变了通信方向,MVP 非常重要的一点是基于测试,P 层不应该有任何 android sdk 的代码,mock M 和 V 层后,可以脱离 android 直接对 P 层进行 Java Test。

  1. 各部分之间的通信,都是双向的。
  2. View 与 Model 不发生联系,都通过 Presenter 传递。
  3. View 非常薄,不部署任何业务逻辑,称为”被动视图”(Passive View),即没有任何主动性,而 Presenter非常厚,所有逻辑都部署在那里。

Java的MVP接口定义

MVC是一种架构思想,具体去实现这种思想的方法有很多,只要总的逻辑符合这种模式的要求即可。

思路:我们要做的就是将Activity的业务逻辑和视图操作分离出来,让Presenter做一个纯粹的业务逻辑组织者,让View接口负责具体的视图操作,让Model接口负责数据的请求和封装。然后Presenter通过View和Model的接口来组织数据和视图的交互。

1
2
3
public interface IModel {
//.....定义一些共用的接口
}

定义Model接口,来约束数据请求层的基本逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface IView {

/**
* 显示loading
*/
void showLoading();
/**
* 显示错误
*/
void showError(int Code, String msg);

//.....定义其它一些共用的接口
}

定义View接口来约束和处理公共视图逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
public interface IPresenter<V extends IView> {

/**
* 将视图依附到展示器(Presenter)
* @param mvpView
*/
void attachView(V mvpView);

/**
* 解除依附关系
*/
void dettachView();
}

通过泛型来约束视图层和模型(这里忽略了Model的依赖关系)的依赖,并在子类中实现实际业务逻辑。

Kotlin的MVP接口定义

上面的结构基本上实现了MVP结构,事实上我们可以拓展一下上面的代码,来抽取出来公共的实现(封装在抽象类中),在实际业务中继承即可,这样我们在具体的业务类中只需要关系特有的功能。

下面先看看View视图是怎么实现这种层层抽取公共实现的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//定义基础视图接口
interface View{

fun getActivity(): Activity

fun getContext(): Context

fun showProgress(message: String)

fun dismissProgress()

fun toastSuccess(message: String)

fun toastFail(message: String)

fun isProgressShow(): Boolean
}

//定义MVP视图接口
interface MvpView : View{

fun init(container: android.view.View)

fun init(container: Activity)

//实现公共的Progress视图
abstract class ProgressView: MvpView{

//TODO 这里实现所有和Progress视图相关的方法
}
}

上面的ProgressView就实现了公共的抽取。我们还可以同样的方式继续来继承接口实现下一层次的抽取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//继续定义公共视图逻辑
interface BaseMainView : MvpView{

protected abstract fun getMainTab(): ArrayList<MainTab>

abstract class Base : MvpView.ProgressView(){

}
}

interface MainView : BaseMainView{
fun toggleRedNote()
fun dismissRedNote()

abstract class Base : BaseMainView.Base(), MainView{

}
}

这样就自下而上的层层实现具体的视图逻辑,如果在开发过程中需要添加公共的视图接口,不需要修改原来的代码,只需要增加额外的接口并实现即可。

业务逻辑层和模型层同样可以这样实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface Presenter<V: View>{

fun getView(): V

fun unsubscribeAll()
}

private interface SubscriberWatcher {

fun watchSubscription(subscription: Subscription)
}

abstract class Preset<V: View>(view: V): Presenter<V>, SubscriberWatcher{

}