1,MVC的概念
Model: 外部世界的建模
这个描述的比较抽象,但是把这个定义放在一个面向对象的Android应用程序设计中,就很简单了,即:管理 符合程序需要的数据 的对象;
例如在一个展示人类的姓名、年龄的应用程序中,我们所构建的人类的这个对象中,应该包括姓名、年龄这两个数据,并且提供数据的获取+更新等方法。
View: 可视化的程序反馈
对于一个Android应用程序来说,就是用户看到的View(TextView、ImageView、Button等)。
Controller: 根据用户行为,改变Model+View
用户行为之后的一系列的操作。
这段话把MVC对应的各个模块描述了一个编,并没有对MVC有一个整体的概念输出,而网上的很多人一张口不是“MVC框架”,就是“MVC”设计模式,
相信大家在了解Smalltalk中关于MVC的定义之后,一定会觉得这些定义略荒唐。
2,MVC的性质
上面说的,不是框架,也不是设计模式,那MVC应该定义为啥?
MVC基于职责分离思想,设计于图形界面应用程序的开发,而《Design Patterns》中也并没有把MVC纳入为一种设计模式,Smalltalk把MVC作为一种triad,
而在MVC的交互中,使用了当前非常流行的 观察者 设计模式,所以我得出的MVC最原始定义应该是:
“一种基于责任分离思想的、旨在高效开发图形界面程序的、合理使用设计模式的模块的合集。”
其实关于MVC整体的属性,众说纷纭,并且都有独特见解,这里的描述只是我个人的理解,如有异议,欢迎指点。
3,MVC之间的交互
(一)、MVC之间的依赖关系(图片来自网络)
1,View,Controller都依赖于Model;
2,View 依赖于 Controller。
(二)、MVC之间的调用关系(图片来自网络)
1,View把用户行为传递给Controller,不做任何业务逻辑相关的操作;
2,Controller处理用户行为,并通过Model提供的数据管理方法,进行数据更新;
3,Model数据发生变化的时候,通过观察者模式,通知View,View接受到通知后调用Model的数据获取方法进行更新。
二、MVC在Android中的应用
1,View
UserActivity.java
/**
* 应用程序功能:
* 提供一个Button, 用户点击这个Button后, 把UserModel中<姓名>变成 “Interesting”
* 然后在View中, Toast出UserModel中的<姓名>.
*/
public class UserActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 1, 用户点击Button之后, 传递Event给UserController.
UserController.getInstance().onUserClickButton();
}
});
UserModel.getInstance().setOnChangedListener(new OnChangedListener() {
@Override
public void onChanged() {
// 4, 接受到了UserModel的update通知
// View通过UserModel提供的name获取方法,获取name,并且Toast弹出.
Toast.makeText(getBaseContext(),
UserModel.getInstance().getName(),
Toast.LENGTH_LONG).show();
}
});
}
}
2,Controller
UserController.java
public class UserController {
private static UserController mInstance;
public static UserController getInstance() {
if(mInstance == null) {
mInstance = new UserController();
}
return mInstance;
}
public void onUserClickButton() {
// 2, UserController接受到Event, 但不知道具体是哪个View发来的,
// 然后通过UserModel的更新方法, 更新name为 “Interesting”.
UserModel.getInstance().setName("Interesting");
}
}
3,Model
UserModel.java
public class UserModel {
private static UserModel mInstance;
public static UserModel getInstance() {
if(mInstance == null) {
mInstance = new UserModel();
}
return mInstance;
}
private String name;
public void setName(String name) {
// 3, UserModel把自己的name更新, 不知道是哪个Controller调用的
this.name = name;
// 更新之后, 通知观察者, 已经发生更新, 也不知道观察者到底是谁.
listener.onChanged();
}
public String getName() {
return this.name;
}
private OnChangedListener listener;
public void setOnChangedListener(OnChangedListener listener) {
this.listener = listener;
}
public interface OnChangedListener {
void onChanged();
}
}
根据上面的实例代码,可以总结出MVC的优势:
1,一个Controller可以为多个View服务,Controller提供了业务逻辑的接口,并不关注调用者是谁;
2,Controller中的业务逻辑发生变化时,View和Model都无须改动(当Controller被替换时,View需要更换Controller);
3,当Controller更新Model时,View会通过观察者的方式同步更新,如果还有其他界面观察这个View时(实现方式类似于TextWatcher),其他的View也会同时更新。
这里有人会对第2点产生这样的质疑:
假如多个View使用Controller,当Controller需要更换时,且不是每个View都要改一遍?耦合性这么大,怎么还能称得上优势?
是的,如果Controller被替换,所有的引用方都需要更改;但是大家要知道 MVC的责任分离思想 和 代码实现上的耦合 是没有任何冲突的:
① MVC旨在让GUI应用开发时,更加灵活——多变的业务逻辑可以随便变动;更加稳定——各个模块实现正确,整个流程即可正常运转;
出现问题时,定位更加迅速,只要测试模块间的请求+返回数据,就可以迅速分析出是哪个模块出现了问题,然后再进行内部分析。
② Java代码现实上的耦合,我们可以通过接口化来解决View和实际逻辑Controller直接的耦合,所有语言实现上的问题,通过语言本身提供的机制解决即可。
那么MVC的缺点是什么:
1,View是通过观察者模式同步Model的更新的,也就是说View始终要看着Model,自然地方想复用这个View的时候,势必要放弃观看之前的Model,所以这里的View是无法复用的;
2,Controller单元测试比较困难,测试很多情况下都需要依赖最终的View显示结果,如果脱离View只对Controller进行测试,光看请求前后的数据,很难测试业务逻辑的准确性。
三、MVP+MVVM的诞生
1,MVP
上面分析了MVC的优缺点,我们发现MVC中的View无法复用,于是MVP就诞生了,思路很简单:View不再观察Model还能同步更新,其他优点保持不变。
(一)、MVP之间的依赖关系(图片来自网络)
1,Controller依赖于Model,对比与MVC,View不再依赖Model;
2,View 依赖于 Controller。
(二)、MVP之间的调用关系(图片来自网络)
1,View把用户行为传递给Controller,不做任何业务逻辑相关的操作;(同MVC)
2,Controller处理用户行为,并通过Model提供的数据管理方法,进行数据更新;(同MVC)
3,Controller通过观察者模式接受到Model的变化时,获取Model的数据,通过View提供的接口更新View。(优化点)
(三)、MVP在Android中的应用
View
UserActivity.java
/**
* 应用程序功能:
* 提供一个Button, 用户点击这个Button后, 把UserModel中<姓名>变成 “Interesting”
* 然后在View中, Toast出UserModel中的<姓名>.
*/
public class UserActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 1, 用户点击Button之后, 传递Event给UserController, 同时提供View更新接口.
Presenter.getInstance(viewCallback).onUserClickButton();
}
});
}
private ViewCallback viewCallback = new ViewCallback() {
@Override
public void setName(String name) {
// 5, 在View更新的接口中, 更新View
Toast.makeText(getBaseContext(), name, Toast.LENGTH_LONG).show();
}
};
}
Presenter
Presenter.java
public class Presenter {
private static Presenter mInstance;
public static Presenter getInstance(ViewCallback viewCallback) {
if(mInstance == null) {
mInstance = new Presenter(viewCallback);
}
return mInstance;
}
private OnChangedListener onChangedListener = new OnChangedListener() {
@Override
public void onChanged() {
// 4, 接受到了UserModel的update通知
// UserController通过UserModel提供的name获取方法,获取name
// 并通过View提供的接口,完成View的更新
viewCallback.setName(UserModel.getInstance().getName());
}
};
private ViewCallback viewCallback;
public Presenter(ViewCallback viewCallback) {
// 获得一个更新View的接口,并不知道是哪个View
this.viewCallback = viewCallback;
// 不同于MVC, MVP在Presenter中观察Model, 而不是View.
UserModel.getInstance().setOnChangedListener(onChangedListener);
}
public void onUserClickButton() {
// 2, UserController接受到Event, 但不知道具体是哪个View发来的,
// 然后通过UserModel的更新方法, 更新name为 “Interesting”.
UserModel.getInstance().setName("Interesting");
}
public interface ViewCallback {
void setName(String name);
}
}
Model
UserModel.java
public class UserModel {
private static UserModel mInstance;
public static UserModel getInstance() {
if(mInstance == null) {
mInstance = new UserModel();
}
return mInstance;
}
private String name;
public void setName(String name) {
// 3, UserModel把自己的name更新, 不知道是哪个Controller调用的
this.name = name;
// 更新之后, 通知观察者, 已经发生更新, 也不知道观察者到底是谁.
listener.onChanged();
}
public String getName() {
return this.name;
}
private OnChangedListener listener;
public void setOnChangedListener(OnChangedListener listener) {
this.listener = listener;
}
public interface OnChangedListener {
void onChanged();
}
}
根据实现的代码可以发现MVP的优势
① View不再观察Model,只要提供一套完善的View更新接口,即可被其他Presenter使用,说明View是可服用的;
② Presenter可以服务多个View,也对View无知,使用服务的View只要提供更新接口即可;
③ Presenter发生变化时,View和Model仍然不需要做任何改动;
④ 当Model发生变化的时候,观察Model的Presenter会同步更新View,虽然多走了一步,但是效果同于MVC。
但是MVP的缺点也非常明显(第2点针对于android):
① Presenter要做的事情变多了,既要更新model,又要更新view,业务复杂,界面元素较多时,会非常的笨重;
② View虽然只是提供的更新接口要非常的细,否则难以复用,而一旦更新接口细了,View元素一多,View也会显得臃肿。
为了便于大家对于MVP的理解,这里还有一个例子,http://antonioleiva.com/mvp-android/
2,MVVM
MVVM相对于MVP来说,更加先进,Presenter被替换成ViewModel,原有的Presenter→Model + Presenter→View 被改成 ViewModel → Model,而View更新部分则交给了第三方框架自动实现;
简单的说就是MVVM相对于MVP来说:分工更加明确,实现更加简洁。
但是MVVM在android的使用会涉及到xml的变动,需要开发者掌握原有MVP的设计思想,并且熟悉2015年 Google IO大会推出的Data Binding:
官网介绍地址:https://developer.android.com/tools/data-binding/guide.html
如果打不开android官网,下载了android-sdk的朋友可以直接查看本地如: sdk安装的路径/android-sdk/docs/tools/data-binding/guide.html
中文翻译版本: http://www.jianshu.com/p/b1df61a4df77
看完之后,为了便于大家理解,这里有一个例子: https://github.com/LyndonChin/MasteringAndroidDataBinding