MVC、MVP、MVVM,我到底该怎么选
原作者:
AndroFarmer
版权声明:本文版权归微信公众号
玉刚说
所有,未经许可,不得以任何形式转载
前言
MVC、MVP、MVVM 是我们工作和面试中都比较重要的一块,但很多时候我们却有点迷惑。比如看了好多篇文章都搞不懂 MVC 到底是个啥本来想写个 MVP 写着写着就变成 MVC 了,到底 Databing 和 MVVM 之间有啥见不得人的关系。本篇文章主要从发展的角度来介绍,如 mvp,mvvm 的出现都是为了解决前者的哪些问题。如果你有同样的疑问,本篇文章可能会给你带来一点收获。但是架构和设计模式相对来说不是那么容易捉摸透的东西,很多需要经过实践才能体会,另外由于本人水平有限,如果写的不对或者不严谨的地方,请不要打我。
MVC
可能由于 MVP、MVVM 的兴起,MVC 在 android 中的应用变得越来越少了,但 MVC 是基础,理解好 MVC 才能更好的理解 MVP,MVVM。因为后两种都是基于 MVC 发展而来的。
1、MVC 眼花缭乱设计图
我们从网上搜索 mvc 相关资料时,如果你多看几篇文章的话可能会发现,好像他们介绍的设计图都不太一样,这里罗列了大部分的设计图
2、MVC 设计图解释
到底上面列出的设计图哪个才是对的。其实都是对的。为什么这么说呢,这得从 mvc 的发展说起。 MVC 框架模式最早由 Trygve Reenskaug 于 1978 年在 Smalltalk-80 系统上首次提出。经过了这么多年的发展,当然会演变出不同的版本,但核心没变依旧还是三层模型 Model-View-Control。
3、MVC 三层之间的关系
箭头→代表的是一种事件流向,并不一定要持有对方,比如上图中 model→view 的事件流向,view 可以通过注册监听器的形式得到 model 发来的事件。在设计中 model view controller 之间如果要通讯,尽量设计成不直接持有,这样方便复用。也符合 mvc 的设计初衷 在 android 中三者对应的关系如下:
视图层(View) 对应于 xml 布局文件和 java 代码动态 view 部分
控制层(Controller) MVC 中 Android 的控制层是由 Activity 来承担的,Activity 本来主要是作为初始化页面,展示数据的操作,但是因为 XML 视图功能太弱,所以 Activity 既要负责视图的显示又要加入控制逻辑,承担的功能过多。
模型层(Model) 针对业务模型,建立的数据结构和相关的类,它主要负责网络请求,数据库处理,I/O 的操作。
由于 android 中有个 god object 的存在 activity,再加上 android 中 xml 布局的功能性太弱,所以 activity 承担了绝大部分的工作。所以在 android 中 mvc 更像是这种形式:
因为 activity 扮演了 controller 和 view 的工作,所以 controller 和 view 不太好彻底解耦,但是在一定程度上我们还是可以解耦的。 Talk is cheap. Show me the code. 扯了这么多,我们来看点代码。
4、MVC sample
通过代码来看下,mvc 在 android 中的实现
结构很简单,这里介绍下其中的关键代码
public interface BaseModel {void onDestroy();}
BaseModel 顾名思义就是所有业务逻辑 model 的父类,这里的 onDestroy()方法用于跟 activity 或者 fragment 生命周期同步,在 destroy 做一些销毁操作
public interface Callback1<T> {void onCallBack(T t);}public interface Callback2<T,P> {void onCallBack(T t,P p);}
Callback 是根据 View 或者 Controller 调用 Model 时回调的参数个数选择使用
public class SampleModel implements BaseModel{
public void getUserInfo(String uid,Callback1<UserInfo> callback){
UserInfo userInfo= new HttpUtil<UserInfo>().get(uid);callback.onCallBack(userInfo);
}
@Overridepublic void onDestroy() {
}
public class UserInfo{private int age;private String name;
public int getAge() {return age;}
public void setAge(int age) {this.age = age;}
public String getName() {return name;}
public void setName(String name) {this.name = name;}}}
SampleModel 是我们业务逻辑的具体实现
public class SampleActivity extends AppCompatActivity {private SampleModel sampleModel;Button button;EditText textView;TextView tvAge,tvName;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_sample);sampleModel=new SampleModel();button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {getUserInfo(textView.getText().toString());}});
}
@Overrideprotected void onDestroy() {super.onDestroy();sampleModel.onDestroy();}
/**
获取用户信息
@param uid*/private void getUserInfo(String uid){sampleModel.getUserInfo(uid, new Callback1<SampleModel.UserInfo>() {@Overridepublic void onCallBack(SampleModel.UserInfo userInfo) {setDataToView(userInfo);}});}
/**
设置用户信息到 view*/private void setDataToView(SampleModel.UserInfo userInfo){tvAge.setText(userInfo.getAge());tvName.setText(userInfo.getName());}
}
前面说了 Activity 充当 View 和 Controller,但是我们依然要区分到底哪一部分是 View 的操作,哪一部分是 Controller 的操作。 我们分析下事件的流向
button 点击事件的触发:View→Controller 获取用户信息事件的触发:Controller→Model 绑定用户信息到 View:Controller→View 至此 MVC 就讲完了
5、MVC 总结
我们这里根据 sample 来总结下:
具有一定的分层,model 彻底解耦,controller 和 view 并没有解耦
层与层之间的交互尽量使用回调或者去使用消息机制去完成,尽量避免直接持有
controller 和 view 在 android 中无法做到彻底分离,但在代码逻辑层面一定要分清
业务逻辑被放置在 model 层,能够更好的复用和修改增加业务
MVP
1、MVP 说明
MVP 跟 MVC 很相像,文章开头列出了很多种 MVC 的设计图,所以根据 MVC 的发展来看,我们把 MVP 当成 MVC 来看也不为过,因为 MVP 也是三层,唯一的差别是 Model 和 View 之间不进行通讯,都是通过 Presenter 完成。 前面介绍 MVC 的时候提到了算是致命缺点吧,在 android 中由于 activity(god object)的存在,Controller 和 View 很难做到完全解耦。但在 MVP 中就可以很好的解决这个问题 看下 MVP 的设计图:
一般情况下就这两种
2、MVP Sample
依然延续 MVC 的例子,修改下结构通过 MVP 去实现,看下项目代码结构:
callback,http 包下内容基本一致,主要看下不同的地方
public interface BasePresenter {void onDestroy();}
BasePresenter 类似于 MVC 中的 BaseModel,主要负责业务逻辑的实现。我们这里没有把业务逻辑放在 Model 里去实现,当然把主要业务逻辑放在 Model 中去实现也是可以的。google 的 MVP 实现方案是把业务逻辑放在 presenter 中,弱化 Model,我们这里也是这样做的。
public interface BaseView<P extends BasePresenter> {void setPresenter(P presenter);}
BaseView 是所有 View 的父类,将 android 中的 view 抽象话出来,只有跟 view 相关的操作都由 baseView 的实现类去完成。
public class SampleContract {public static class Presenter implements BasePresenter{public void getUserInfo(String uid,Callback1<SampleModel.UserInfo> callback){SampleModel.UserInfo userInfo= new HttpUtil<SampleModel.UserInfo>().get(uid);callback.onCallBack(userInfo);}
@Overridepublic void onDestroy() {
}}public interface View extends BaseView<Presenter>{void setDataToView(SampleModel.UserInfo userInfo);}}
Contract 契约类这是 Google MVP 与其他实现方式的又一个不同,契约类用于定义同一个界面的 view 的接口和 presenter 的具体实现。好处是通过规范的方法命名和注释可以清晰的看到整个页面的逻辑。
public class SampleActivity extends AppCompatActivity implements SampleContract.View{private SampleContract.Presenter mPresenter;Button button;EditText textView;TextView tvAge,tvName;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_sample);setPresenter(new SampleContract.Presenter());
button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {mPresenter.getUserInfo(textView.getText().toString(), new Callback1<SampleModel.UserInfo>() {@Overridepublic void onCallBack(SampleModel.UserInfo userInfo) {setDataToView(userInfo);}});}});
}
@Overrideprotected void onDestroy() {super.onDestroy();mPresenter.onDestroy();}
@Overridepublic void setDataToView(SampleModel.UserInfo userInfo) {tvAge.setText(userInfo.getAge());tvName.setText(userInfo.getName());}
@Overridepublic void setPresenter(SampleContract.Presenter presenter) {mPresenter=presenter;}}
这里的 SampleActivity 实现了 SampleContract.View 只是作为 View 存在的。虽然看起来,跟 MVC 中的实现很相似,但却有本质的区别。mPresenter 为 Model 和 View 之间交互的桥梁。Presenter 跟 View 相互持有,这里 SampleActivity 实现了 SampleContract.View,mPresenter 作为 SampleActivity 的成员变量,SampleActivity 当然持有 mPresenter,由于 mPresenter 是非静态的成员标量,因此默认持有 SampleActivity 的引用。
3、MVP 总结
通过引入接口 BaseView,让相应的视图组件如 Activity,Fragment 去实现 BaseView,实现了视图层的独立,通过中间层 Preseter 实现了 Model 和 View 的完全解耦。MVP 彻底解决了 MVC 中 View 和 Controller 傻傻分不清楚的问题,但是随着业务逻辑的增加,一个页面可能会非常复杂,UI 的改变是非常多,会有非常多的 case,这样就会造成 View 的接口会很庞大。
MVVM
1、MVVM 说明
MVP 中我们说过随着业务逻辑的增加,UI 的改变多的情况下,会有非常多的跟 UI 相关的 case,这样就会造成 View 的接口会很庞大。而 MVVM 就解决了这个问题,通过双向绑定的机制,实现数据和 UI 内容,只要想改其中一方,另一方都能够及时更新的一种设计理念,这样就省去了很多在 View 层中写很多 case 的情况,只需要改变数据就行。 先看下 MVVM 设计图:
一般情况下就这两种情况,这看起来跟 MVP 好像没啥差别,其实区别还是挺大的,在 MVP 中 View 和 presenter 要相互持有,方便调用对方,而在 MVP 中 View 和 ViewModel 通过 Binding 进行关联,他们之前的关联处理通过 DataBinding 完成。
2、MVVM 与 DataBinding 的关系
一句话表述就是,MVVM 是一种思想,DataBinding 是谷歌推出的方便实现 MVVM 的工具。在 google 推出 DataBinding 之前,因为 xml layout 功能较弱,想实现 MVVM 非常困难。而 DataBinding 的出现可以让我们很方便的实现 MVVM。
3、DataBinding 简介
DataBinding 是实现视图和数据双向绑定的工具,这里简单介绍下基本用法,详细用法可以参照官方:https://developer.android.com/topic/libraries/data-binding/ 启用 DataBinding,只需要在 gradle 文件中添加如下代码:
android {
dataBinding{enabled true}}
通过 DataBindingUtil 可以动态生成一个 ViewDataBinding 的子类,类名以 layout 文件名大写加 Binding 组成,如:
ActivitySampleMvvmBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_sample_mvvm);
在 layout 中需要我们配置,每个控件绑定的实体对象,以 layout 进行包裹,data 中配置变量名和类型,通过 @{}或 @={}的方式进行引用,其中 @={}的方式表示双向绑定。目前支持双向绑定的控件如下:
AbsListView android:selectedItemPosition CalendarView android:date CompoundButton android:checked DatePicker android:year, android:month, android:day NumberPicker android:value RadioGroup android:checkedButton RatingBar android:rating SeekBar android:progress TabHost android:currentTab TextView android:text TimePicker android:hour, android:minute
<?xml version="1.0" encoding="utf-8"?><layout xmlns:android="http://schemas.android.com/apk/res/android"><da
ta ><variablename="user"type="com.androfarmer.mvvm.model.SampleModel.UserInfo"></variable>
评论