本文分析了 MVC、MVP 的概念,并以一个 HelloWorld 的例子来深入说明。
一、明确的概念
MVC、MVP 我们都知道,是一种架构模式。但它们的明确定义到底是什么呢,网上各式各样的流程图,各式各样的箭头指向,看得人头晕眼花,云里雾里。经过漫长的寻觅,我终于在这里找到一句话,解答了我的疑惑:不幸的是,MVC、MVP 都没有被广泛接受的定义
。
仔细想想,可不就是这样吗,MVC、MVP 只是一种思想,具体的实现方式当然不会完全一致,而这个思想唯一明确的、被广泛接受的概念只有一个,那就是:把代码分为三块
。
代码分为的三块:
Model:数据
View:界面
Controller/Presenter:界面和数据的管理者/控制者/主持者
从这个角度来说,MVC、MVP 其实是一样的,只是命名不同而已。
二、Android HelloWorld 的实例
以一个 Android 的 HelloWorld 的例子来说明:
public class HelloActivity extends AppCompatActivity {
private TextView mTvHello;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_hello);
mTvHello = findViewById(R.id.tv_hello);
mTvHello.setText("hello world")
}
}
这里面的 M、V、C:
“helloWorld” ->
model
textView ->view
textView.setText(“helloWorld”) ->controller/Presenter
textView.setText("helloWorld")
在 Activity 中执行,所以 Activity 是 controller/Presenter,"helloWorld"
、textView.setText("helloWorld")
都在 Activity 中,所以它也包含了 View、model。
当 model、view 变得越来越多、越来越大,controller/Presenter (Activity)的代码就会变得很多。所以人们会很想将它们区分开(解耦),View 就是 view,Model 就是 Model,controller/Presenter 就是 controller/Presenter。
2.1 一种常见的分离方式
下面来看一种很常见的分离方式。(这就是被广为流传的 MVP 模式,为了方便理解,没有使用接口)。
Model 是最容易拆出来的:
public class HelloModel {
public String getHelloText() {
return "hello world";
}
}
View:
public class HelloActivity extends AppCompatActivity {
private TextView mTvHello;
private HelloPresenter mPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_hello);
mTvHello = findViewById(R.id.tv_hello);
mPresenter = new HelloPresenter(this);
mPresenter.setHelloText();
}
public void setHelloText(String helloText) {
mTvHello.setText(helloText);
}
}
Presenter:
public class HelloPresenter {
private HelloModel mHelloModel;
private HelloActivity mHelloView;
public HelloPresenter(HelloActivity helloView) {
mHelloView = helloView;
mHelloModel = new HelloModel();
}
public void setHelloText() {
mHelloView.setHelloText(mHelloModel.getHelloText());
}
}
可以看出,在这种分离方式中,是将 Activity 作为 View,将操作 View、Model 的方法提到了单独的 Presenter 类中。
这样看上去貌似没有什么问题,但是总觉得哪里不太对劲。
我们看看 View (Activity)里的方法,在 onCreate 中调了 mPresenter.setHelloText()
,而 mPresenter.setHelloText()
内部又调了 View(Activity)自己的 setHelloText() 方法。这不是绕了一圈又回来了吗。
既然这样,为什么 View(Activity)不直接调自己的 setHelloText() 方法呢?因为它只是一个 View,一个动作不应该由它来发起。可是通过 presenter 再去调自己的方法不还是间接地发起了一个动作吗。
产生这样的矛盾的原因就在于:onCreate() 方法天生就属于 Presenter,即 Activity 天生就是 Presenter
。我们通过这种方式抽离 Presenter,只能抽取一部分,总会剩下一部分留在 Activity 里(如生命周期函数)。
所以这样的解耦仍是不彻底的,Activity 依然是 View 和 Presenter 的结合体,在某些情况下,反而会造成逻辑的冗余(如上述 Activity-Presenter-Activity 的调用链)。
有人可能会说,既然 onCreate 是属于 Presenter 的,那在 Presenter 增加一个方法 onCreate() 在 Activity 的 onCreate() 方法中调用它。这样没问题,这相当于把 Activity 的 onCreate() 方法当做了 View 的类似 onClick() 一样的回调。View 回调 onCreate()、onClick(),Presenter 执行 onCreate()、onClick() 的真实逻辑,也说得通。但问题是,Activity 中除了 onCreate(),还有太多的方法,Activity extends Context
,它本来就是用来与平台交互、与应用的业务交互的。难道都提出来吗,这显然不现实。
2.2 一种更合理的分离方式
所以,既然 Presenter 提不出来,那 View 呢?好消息是,View 可以,而且很是清晰。
Model 不变,仍是原来的配方:
public class HelloModel {
public String getHelloText() {
return "hello world";
}
}
View:
public class HelloView {
private View mRootView;
private TextView mTvHello;
public HelloView(LayoutInflater inflater) {
mRootView = inflater.inflate(R.layout.activity_hello, null, false);
mTvHello = mRootView.findViewById(R.id.tv_hello);
}
public void setHelloText(String helloText) {
mTvHello.setText(helloText);
}
public View getRootView() {
return mRootView;
}
}
Presenter:
public class HelloActivity extends AppCompatActivity {
private HelloView mHelloView;
private HelloModel mHelloModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 生成 View
mHelloView = new HelloView(getLayoutInflater());
setContentView(mHelloView.getRootView());
// 生成 Model
mHelloModel = new HelloModel();
// 连接 View 和 Model
mHelloView.setHelloText(mHelloModel.getHelloText());
}
}
这种方式,将 Activity 完全作为 Presenter,而将 View 抽出去分离。Activity 不用关心 View 的任何细节,只要调 View 提供的方法就好。
对 mHelloView.setHelloText
的调用也只是两层调用,Activity-View,逻辑更加清晰。
以上就是我对 MVC、MVP 的浅见,如有其它见解,欢迎交流。