Android中MVC、MVP、MVVM

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

推荐阅读更多精彩内容