阿里大牛:选择大于努力,因此MVC、MVP、MVVM,我到底该怎么选?

前言


MVC、MVP、MVVM是咱们工做和面试中都比较重要的一块,但不少时候咱们却有点迷惑。好比看了好多篇文章都搞不懂MVC究竟是个啥原本想写个MVP写着写着就变成MVC了,到底Databing和MVVM之间有啥见不得人的关系。本篇文章主要从发展的角度来介绍,如mvp,mvvm的出现都是为了解决前者的哪些问题。若是你有一样的疑问,本篇文章可能会给你带来一点收获。可是架构和设计模式相对来讲不是那么容易捉摸透的东西,不少须要通过实践才能体会,另外因为本人水平有限,若是写的不对或者不严谨的地方,请不要打我。html

MVC


可能因为MVP、MVVM的兴起,MVC在android中的应用变得愈来愈少了,但MVC是基础,理解好MVC才能更好的理解MVP,MVVM。由于后两种都是基于MVC发展而来的。java

一、MVC眼花缭乱设计图

咱们从网上搜索mvc相关资料时,若是你多看几篇文章的话可能会发现,好像他们介绍的设计图都不太同样,这里罗列了大部分的设计图android

二、MVC设计图解释

到底上面列出的设计图哪一个才是对的。其实都是对的。为何这么说呢,这得从mvc的发展提及。面试

MVC框架模式最先由Trygve Reenskaug 于1978年在Smalltalk-80系统上首次提出。通过了这么多年的发展,固然会演变出不一样的版本,但核心没变依旧仍是三层模型Model-View-Control。数据库

三、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. 扯了这么多,咱们来看点代码。

四、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 void  getUserInfo(String uid,Callback1<UserInfo> callback)
    {

        UserInfo userInfo= new HttpUtil<UserInfo>().get(uid);
        callback.onCallBack(userInfo);

    }

    @Override
    public 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;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sample);
        sampleModel=new SampleModel();
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                getUserInfo(textView.getText().toString());
            }
        });

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        sampleModel.onDestroy();
    }

    /**
     * 获取用户信息
     * @param uid
     */
    private void getUserInfo(String uid)
    {
        sampleModel.getUserInfo(uid, new Callback1<SampleModel.UserInfo>() {
            @Override
            public 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就讲完了

五、MVC总结

咱们这里根据sample来总结下:

  • 具备必定的分层,model完全解耦,controller和view并无解耦
  • 层与层之间的交互尽可能使用回调或者去使用消息机制去完成,尽可能避免直接持有
  • controller和view在android中没法作到完全分离,但在代码逻辑层面必定要分清
  • 业务逻辑被放置在model层,可以更好的复用和修改增长业务

MVP


一、MVP说明

MVP跟MVC很相像,文章开头列出了不少种MVC的设计图,因此根据MVC的发展来看,咱们把MVP当成MVC来看也不为过,由于MVP也是三层,惟一的差异是Model和View之间不进行通信,都是经过Presenter完成。

前面介绍MVC的时候提到了算是致命缺点吧,在android中因为activity(god object)的存在,Controller和View很难作到彻底解耦,但在MVP中就能够很好的解决这个问题。看下MVP的设计图:

通常状况下就这两种。

二、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);
        }

        @Override
        public 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;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sample);
        setPresenter(new SampleContract.Presenter());

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mPresenter.getUserInfo(textView.getText().toString(), new Callback1<SampleModel.UserInfo>() {
                    @Override
                    public void onCallBack(SampleModel.UserInfo userInfo) {
                        setDataToView(userInfo);
                    }
                });
            }
        });

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mPresenter.onDestroy();
    }

    @Override
    public void setDataToView(SampleModel.UserInfo userInfo) {
        tvAge.setText(userInfo.getAge());
        tvName.setText(userInfo.getName());
    }


    @Override
    public 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的引用。

三、MVP总结

经过引入接口BaseView,让相应的视图组件如Activity,Fragment去实现BaseView,实现了视图层的独立,经过中间层Preseter实现了Model和View的彻底解耦。MVP完全解决了MVC中View和Controller傻傻分不清楚的问题,可是随着业务逻辑的增长,一个页面可能会很是复杂,UI的改变是很是多,会有很是多的case,这样就会形成View的接口会很庞大。

MVVM


一、MVVM说明

MVP中咱们说过随着业务逻辑的增长,UI的改变多的状况下,会有很是多的跟UI相关的case,这样就会形成View的接口会很庞大。而MVVM就解决了这个问题,经过双向绑定的机制,实现数据和UI内容,只要想改其中一方,另外一方都可以及时更新的一种设计理念,这样就省去了不少在View层中写不少case的状况,只须要改变数据就行。先看下MVVM设计图:

通常状况下就这两种状况,这看起来跟MVP好像没啥差异,其实区别仍是挺大的,在MVP中View和presenter要相互持有,方便调用对方,而在MVP中 View和ViewModel经过Binding进行关联,他们以前的关联处理经过DataBinding完成。

二、MVVM与DataBinding的关系

一句话表述就是,MVVM是一种思想,DataBinding是谷歌推出的方便实现MVVM的工具。在google推出DataBinding以前,由于xml layout功能较弱,想实现MVVM很是困难。而DataBinding的出现可让咱们很方便的实现MVVM。

三、DataBinding简介

DataBinding是实现视图和数据双向绑定的工具,这里简单介绍下基本用法,详细用法能够参照官方:https://developer.android.com...,只须要在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">
    <data >
        <variable
            name="user"
            type="com.androfarmer.mvvm.model.SampleModel.UserInfo">
        </variable>
    </data>
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <TextView
        android:id="@+id/tv_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@={user.name}"
        />
    <TextView
        android:id="@+id/tv_age"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@={user.age+``}"
        />

</LinearLayout>
</layout>

以上为具体在xml的用法展现

public static class UserInfo  extends BaseObservable
    {
        private int age;
        private String name;

        @Bindable
        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
            notifyPropertyChanged(BR.age);
        }

        @Bindable
        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
            notifyPropertyChanged(BR.name);
        }
    }

为了实现双向绑定还须要对数据实体类作处理,继承BaseObservable,对读写方法作@Bindable和notifyPropertyChanged处理。还能够直接使用官方提供的泛型可观察对象 ObservableField,好比:
private ObservableField name=new ObservableField<>()

四、MVVM Sample

MVVM中跟MVP中同样,将三层划分的很清楚,Activity和xml layout充当View,ViewModel处理业务逻辑以及获取数据,弱化Model。不少代码跟前面相似,这里只列出核心代码,ViewModel层的:

private SampleViewModel mSampleViewModel;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivitySampleMvvmBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_sample_mvvm);
        mSampleViewModel=new SampleViewModel(binding);
        setDataToView();
    }
    private void setDataToView()
    {
        mSampleViewModel.getUserInfo("uid", new Callback1<SampleModel.UserInfo>() {
            @Override
            public void onCallBack(SampleModel.UserInfo userInfo) {
                mSampleViewModel.mViewDataBinding.setUser(userInfo);
            }
        });
    }

xml layout代码在上面介绍databing的用法时已给出,经过上面代码咱们就将数据UserInfo跟View进行绑定了。好比咱们更新用户信息,能够直接对View上的属性进行修改:mSampleViewModel.mViewDataBinding.tvName.setText("rose");也能够经过修改UserInfo实体类的字段信息:mSampleViewModel.mViewDataBinding.setUser(userInfo);

今后告别MVP中View层好多接口的问题,让View变得更简洁,修改任何一方,二者都会保持数据同步。

五、MVVM 总结

看起来MVVM很好的解决了MVC和MVP的不足,可是因为数据和视图的双向绑定,致使出现问题时不太好定位来源,有可能数据问题致使,也有可能业务逻辑中对视图属性的修改致使。若是项目中打算用MVVM的话能够考虑使用官方的架构组件ViewModel、LiveData、DataBinding去实现MVVM

关于MVC,MVP,MVVM如何选择的探讨


前面在介绍MVC、MVP、MVVM时并无去详细列出他们的优缺点,主要缘由有两个:

  1. 网上这方面总结的仍是挺多的
  2. 其实关于架构,设计,模块化等等,它们的优缺点没有绝对的,主要看实现者如何去作

好比在mvp中咱们要实现根据业务逻辑和页面逻辑作不少Present和View的具体实现,若是这些case太多,会致使代码的可读性变差。可是经过引入contract契约类,会让业务逻辑变得清晰许多。所以无论是用哪一种设计模式,只要运用得当,均可以达到想要的结果。

若是非要说怎么选的话,以我浅薄的知识建议以下:

  1. 若是项目简单,没什么复杂性,将来改动也不大的话,那就不要用设计模式或者架构方法,只须要将每一个模块封装好,方便调用便可,不要为了使用设计模式或架构方法而使用。
  2. 对于偏向展现型的app,绝大多数业务逻辑都在后端,app主要功能就是展现数据,交互等,建议使用mvvm。
  3. 对于工具类或者须要写不少业务逻辑app,使用mvp或者mvvm均可。
  4. 若是想经过一个项目去学习架构和设计模式,建议用MVC而后在此基础上慢慢挖掘改进。最后你可能发现,改进的最终结果可能就变成了mvp,mvvm。

PS:代码部分不少只是为了演示具体设计模式原理,部分为伪代码,还有些代码写的不是那么严谨。

本篇文章参考以下:http://www.voidcn.com/article...://www.jianshu.com/p/4830912f5162

做者:任玉刚

相关文章
相关标签/搜索