三种架构模式——MVCMVPMVVM

Posted 非早起选手

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了三种架构模式——MVCMVPMVVM相关的知识,希望对你有一定的参考价值。

目录

前言

一、MVC(Model-View-Controller)

1、简介

2、框架图

二、MVP(Model-View-Presenter)

1、简介

2、框架图

三、MVVM(Model-View-ViewModel)

1、简介

2、框架图

四、总结


前言

MV系列框架中,M和V分别指Model层和View层,但其功能会因为框架的不同而变化。Model层是数据模型,用来存储数据;View层是视图,展示Model层的数据。

虽然在不同的框架中,Model层和View层的内容可能会有所差别,但是其基础功能不变,变的只是 数据的传输方式

一、MVCModel-View-Controller

1、简介

MVC是模型-视图-控制器,它是MVC、MVP、MVVM这三者中最早产生的框架,其他两个框架是以它为基础发展而来的。

MVC的目的就是将M和V的代码分离,且MVC是单向通信,必须通过Controller来承上启下。

Model:模型层,数据模型及其业务逻辑,是针对业务模型建立的数据结构,Model与View无关,而与业务有关。

View:视图层,用于与用户实现交互的页面,通常实现数据的输入和输出功能。

Controller:控制器,用于连接Model层和View层,完成Model层和View层的交互。还可以处理页面业务逻辑,它接收并处理来自用户的请求,并将Model返回给用户。

2、框架图

MVC框架图如图1.1示。

图1.1  MVC框架图

上图可以看出各部分之间的通信是单向的,呈三角形状。

具体MVC框架流程图如图1.2所示。

图1.2  MVC框架流程图

从上图可以看出,Controller层触发View层时,并不会更新View层中的数据,View层的数据是通过监听Model层数据变化自动更新的,与Controller层无关。换言之,Controller存在的目的是确保M和V的同步,一旦M改变,V应该同步更新。

同时,我们可以看到,MVC框架大部分逻辑都集中在Controller层,代码量也集中在Controller层,这带给Controller层很大压力,而已经有独立处理事件能力的View层却没有用到;而且,Controller层与View层之间是一一对应的,断绝了View层复用的可能,因而产生了很多冗余代码。

为了解决上述问题,MVP框架被提出。

二、MVPModel-View-Presenter

1、简介

MVP是模型-视图-表示器,它比MVC框架大概晚出现20年,是从MVC模式演变而来的。它们的基本思想有相同之处:Model层提供数据,View层负责视图显示,Controller/Presenter层负责逻辑的处理。将Controller改名为Presenter的同时改变了通信方向。

Model:模型层,用于数据存储以及业务逻辑。

View:视图层,用于展示与用户实现交互的页面,通常实现数据的输入和输出功能。

Presenter:表示器,用于连接M层、V层,完成Model层与View层的交互,还可以进行业务逻辑的处理。

2、框架图

MVP框架图如图2.1示。

图2.1  MVP框架图

上图可以看出各部分之间的通信是双向的。

在MVC框架中,View层可以通过访问Model层来更新,但在MVP框架中,View层不能再直接访问Model层,必须通过Presenter层提供的接口,然后Presenter层再去访问Model层。

具体MVP框架流程图如图2.2所示。

图2.2  MVP框架流程图

从上图可以看出,View层和Model层互不干涉,View层也自由了很多,所以View层可以抽离出来做成组件,在复用性上就比MVC框架好很多。

但是,由于View层和Model层都需要经过Presenter层,导致Presenter层比较复杂,维护起来也会有一定的问题;而且,因为没有绑定数据,所有数据都需要Presenter层进行“手动同步”,代码量较大,虽然比起MVC框架好很多,但还是有比较多冗余部分。

为了让View层和Model层的数据始终保持一致,MVVM框架出现了。

三、MVVMModel-View-ViewModel

1、简介

MVVM是模型-视图-视图模型。MVVM与MVP框架区别在于:MVVM采用双向绑定:View的变动,自动反映在ViewModel,反之亦然。

Model:数据模型(数据处理业务),指的是后端传递的数据。

View:视图,将Model的数据以某种方式展示出来。

ViewModel:视图模型,数据的双向绑定(当Model中的数据发生改变时View就感知到,当View中的数据发生变化时Model也能感知到),是MVVM模式的核心。ViewModel 层把 Model 层和 View 层的数据同步自动化了,解决了 MVP 框架中数据同步比较麻烦的问题,不仅减轻了 ViewModel 层的压力,同时使得数据处理更加方便——只需告诉 View 层展示的数据是 Model 层中的哪一部分即可。

2、框架图

MVVM框架图如图3.1示。

图3.1  MVVM框架图

上图可以看出各部分之间的通信是双向的,而且我们可以看出,MVVM框架图和MVP框架图很相似,两者都是从View层开始触发用户的操作,之后经过第三层,最后到达Model层。而关键问题就在于这第三层的内容,Presenter层是采用手动写方法来调用或修改View层和Model层;而ViewModel层双向绑定了View层和Model层,因此,随着View层的数据变化,系统会自动修改Model层的数据,反之同理。

具体MVVM框架流程图如图3.2所示。

图3.2  MVVM框架流程图

从上图可以看出,View层和Model层之间的数据传递经过了ViewModel层,ViewModel层并没有对其进行“手动绑定”,不仅使速度有了一定的提高,代码量也减少很多,相比于MVC框架和MVP框架,MVVM框架有了长足的进步。

从3.1框架图可以看出,MVVM框架有大致两个方向:

1、模型-->视图     ——实现方式:数据绑定

2、视图-->模型     ——实现方式:DOM事件监听

存在两个方向都实现的情况,叫做数据的双向绑定。双向数据绑定可以说是一个模板引擎,它会根据数据的变化实时渲染。如图3.3所示,View层和Model层之间的修改都会同步到对方。

图3.3  数据的双向绑定

MVVM模型中数据绑定方法一般有三种:

  • 数据劫持
  • 发布-订阅模式
  • 脏值检查

补充:Vue.js使用的就是数据劫持和发布-订阅模式两种方法。了解Vue.js数据绑定流程前,我们需要了解这三个概念:

  • Observer:数据监听器,用于监听数据变化,如果数据发生改变,不论是在View层还是在Model层,Observer都会知道,然后告诉Watcher。
  • Compiler:指定解析器,用于对数据进行解析,之后绑定指定的事件,在这里主要用于更新视图。
  • Watcher:订阅者。

接下来看看Vue.js数据绑定的流程:首先将需要绑定的数据劫持方法找出来,之后用Observer监听这堆数据,如果数据发生变化,Observer就会告诉Watcher,然后Watcher会决定让那个Compiler去做出相应的操作,这样就完成了数据的双向绑定。

四、总结

从MVC到MVP再到MVVM,这是一个不断进步的过程,后两者都是在MVC基础上发展而来,使用起来更加方便。这三者主要的区别在于除Model层和View层之外的第三层的不同。

十分钟上手MVCMVPMVVM


导读


关于MVC、MVP、MVVM的具体概念和优缺点网上有很多文章讲解,这里不再赘述。

但是对于一个没有实战过这三种架构模式的同学们,当你辛辛苦苦花了几个小时读了关于这三种模式的文章,看了一些彼此分离没有对比性的示例代码。你们真的会很快上手这三种架构模式的写法吗?

本文通过一个三合一demo,关键点代码进行注释,十分钟带你入门。让你分分钟看懂三种模式的区别,快速上手三种写法。

这是一个点击按钮,模拟耗时任务获取数据,然后设置Textview的demo。对,就是这么简单,方便大家入门。



01

MVC


十分钟上手MVC、MVP、MVVM

对于MVC模式,本demo只有两个类:MvcMainActivity和GreetingGeneratorModel。

在MVC模式中,Activity就是Controller,同时也是View的一部分,它会调用Model层进行数据获取。Model层,封装各种数据来源,和View层是直接通信的,它会调用View层里的更新View方法。

十分钟上手MVC、MVP、MVVM


 1// Activity即是Controller,同时也是View的一部分
2public class MvcMainActivity extends AppCompatActivity {
3    TextView greetingTextView;
4    Button helloButton;
5    Button goodbyeButtonClicked;
6
7    @Override
8    protected void onCreate(Bundle savedInstanceState) {
9        super.onCreate(savedInstanceState);
10        setContentView(R.layout.activity_mvcmain);
11        greetingTextView = findViewById(R.id.greetingTextView);
12        helloButton = findViewById(R.id.helloButton);
13        goodbyeButtonClicked = findViewById(R.id.goodbyeButtonClicked);
14        // (1)View传递调用到Controller
15        helloButton.setOnClickListener(new View.OnClickListener() {
16            @Override
17            public void onClick(View view) {
18                // (2)Controller直接调用Model层
19                new GreetingGeneratorModel("HelloWorld", greetingTextView).execute();
20            }
21        });
22        // (1)View传递调用到Controller
23        goodbyeButtonClicked.setOnClickListener(new View.OnClickListener() {
24            @Override
25            public void onClick(View view) {
26                // (2)Controller直接调用Model层
27                new GreetingGeneratorModel("GoodBye", greetingTextView).execute();
28            }
29        });
30    }
31}


 1// Model层,封装各种数据来源,和View层是直接通信的
2public class GreetingGeneratorModel extends AsyncTask<VoidVoidInteger{
3    private String baseText;
4    private TextView greetingTextView;
5
6    public GreetingGeneratorModel(String baseText, TextView greetingTextView) {
7        this.baseText = baseText;
8        this.greetingTextView = greetingTextView;
9    }
10
11    // Simulates computing and returns a random integer
12    @Override
13    protected Integer doInBackground(Void... params) {
14        try {
15            // Simulate computing
16            Thread.sleep(2000);
17        } catch (InterruptedException e) {
18        }
19        return (int) (Math.random() * 100);
20    }
21
22    @Override
23    protected void onPostExecute(Integer randomInt) {
24        // (3)Model层调用View
25        if ("HelloWorld".equals(baseText)) {
26            greetingTextView.setTextColor(Color.RED);
27        } else {
28            greetingTextView.setTextColor(Color.BLUE);
29        }
30        greetingTextView.setText(baseText + randomInt);
31    }
32}


好啦。MVC模式讲完啦,就是这么简单,这也是大家初学Android时最常见的写法。


02

MVP


十分钟上手MVC、MVP、MVVM


在实现MVP模式写法时,我们借助了Mosby这个开源库。通过Mosby我们可以很轻松地实现MVP的写法。

对于MVP模式,本demo有四个类:除了有MvpMainActivity和GreetingGeneratorModel外,新增了HelloWorldView和HelloWorldPresenter。

十分钟上手MVC、MVP、MVVM


可以看到,相比于MVC模式,首先,HelloWorldView定义了操作View的接口。


1// Mvp View 接口,定义对View的操作接口
2public interface HelloWorldView extends MvpView {
3    void showHello(String greetingText);
4
5    void showGoodbye(String greetingText);
6}


MvpMainActivity一方面要调用HelloWorldPresenter层进行数据操作;另一方面要实现HelloWorldView中具体操作View的接口。


 1// View层,视图层
2public class MvpMainActivity extends MvpActivity<HelloWorldViewHelloWorldPresenterimplements HelloWorldView {
3    TextView greetingTextView;
4    Button helloButton;
5    Button goodbyeButtonClicked;
6
7    @Override
8    protected void onCreate(Bundle savedInstanceState) {
9        super.onCreate(savedInstanceState);
10        setContentView(R.layout.activity_mvpmain);
11        greetingTextView = findViewById(R.id.greetingTextView);
12        helloButton = findViewById(R.id.helloButton);
13        goodbyeButtonClicked = findViewById(R.id.goodbyeButtonClicked);
14        helloButton.setOnClickListener(new View.OnClickListener() {
15            @Override
16            public void onClick(View view) {
17                // (1)调用Presenter层数据操作
18                presenter.greetHello();
19            }
20        });
21        goodbyeButtonClicked.setOnClickListener(new View.OnClickListener() {
22            @Override
23            public void onClick(View view) {
24                // (1)调用Presenter层数据操作
25                presenter.greetGoodbye();
26            }
27        });
28    }
29
30    @Override
31    protected void onDestroy() {
32        super.onDestroy();
33    }
34
35    @NonNull
36    @Override
37    public HelloWorldPresenter createPresenter() {
38        return new HelloWorldPresenter();
39    }
40
41    // 实现Mvp View 接口,具体对View的操作
42    @Override
43    public void showHello(String greetingText) {
44        greetingTextView.setTextColor(Color.RED);
45        greetingTextView.setText(greetingText);
46    }
47
48    // 实现Mvp View 接口,具体对View的操作
49    @Override
50    public void showGoodbye(String greetingText) {
51        greetingTextView.setTextColor(Color.BLUE);
52        greetingTextView.setText(greetingText);
53    }
54}


GreetingGeneratorModel仍旧提供各种数据来源,但是要定义回调监听,对Presenter提供接口。


 1// Model层,封装各种数据来源,对Prestener层提供接口
2public class GreetingGeneratorModel extends AsyncTask<VoidVoidInteger{
3    // 异步任务中要定义回调监听
4    public interface GreetingTaskListener {
5        public void onGreetingGenerated(String greetingText);
6    }
7
8    private String baseText;
9    private GreetingTaskListener listener;
10
11    public GreetingGeneratorModel(String baseText, GreetingTaskListener listener) {
12        this.baseText = baseText;
13        this.listener = listener;
14    }
15
16    // Simulates computing and returns a random integer
17    @Override
18    protected Integer doInBackground(Void... params) {
19        try {
20            // Simulate computing
21            Thread.sleep(2000);
22        } catch (InterruptedException e) {
23        }
24        return (int) (Math.random() * 100);
25    }
26
27    @Override
28    protected void onPostExecute(Integer randomInt) {
29        // (3)执行回调关联到Presenter层
30        listener.onGreetingGenerated(baseText + " " + randomInt);
31    }
32}


HelloWorldPresenter既要调用Model层进行数据获取,也要实现GreetingGeneratorModel层的回调监听操作View层更新View。


 1// Presenter层,逻辑控制层
2public class HelloWorldPresenter extends MvpBasePresenter<HelloWorldView{
3    // Greeting Task is "business logic"
4    private GreetingGeneratorModel greetingTask;
5
6    private void cancelGreetingTaskIfRunning() {
7        if (greetingTask != null) {
8            greetingTask.cancel(true);
9        }
10    }
11
12    public void greetHello() {
13        cancelGreetingTaskIfRunning();
14        // 实现Model层的回调监听
15        greetingTask = new GreetingGeneratorModel("HelloWorld"new GreetingGeneratorModel.GreetingTaskListener() {
16            @Override
17            public void onGreetingGenerated(String greetingText) {
18                // (4)调用View层更新View
19                if (isViewAttached())
20                    getView().showHello(greetingText);
21            }
22        });
23        // (2)调用Model层获取数据
24        greetingTask.execute();
25    }
26
27    public void greetGoodbye() {
28        cancelGreetingTaskIfRunning();
29        // 实现Model层的回调监听
30        greetingTask = new GreetingGeneratorModel("Goodbye"new GreetingGeneratorModel.GreetingTaskListener() {
31            public void onGreetingGenerated(String greetingText) {
32                // (4)调用View层更新View
33                if (isViewAttached())
34                    getView().showGoodbye(greetingText);
35            }
36        });
37        // (2)调用Model层获取数据
38        greetingTask.execute();
39    }
40
41    // Called when Activity gets destroyed, so cancel running background task
42    public void detachView(boolean retainPresenterInstance) {
43        super.detachView(retainPresenterInstance);
44        if (!retainPresenterInstance) {
45            cancelGreetingTaskIfRunning();
46        }
47    }
48}



03

MVVM


要使用MVVM模式,首先你需要在app下的buld.gradle中添加DataBinding框架配置:


1    // DataBinding配置
2    dataBinding {
3        enabled = true
4    }


然后对布局文件作出一定调整:根标签是layout,根标签之下增加了一个data标签,它包含两个variable标签。

对于原先的Button按钮通过@{handlers.onClickHello}进行事件绑定前置操作。对于原先的Textview通过@{greetingGeneratorObj.baseText} 进行数据绑定前置操作。


 1<layout xmlns:android="http://schemas.android.com/apk/res/android">
2
3    <data>
4
5        <variable
6            name="handlers"
7            type="com.example.architecturalpatterndemo.mvvm.MyHandlers" />

8
9        <variable
10            name="greetingGeneratorObj"
11            type="com.example.architecturalpatterndemo.mvvm.GreetingGeneratorObj" />

12    </data>
13
14    <LinearLayout
15        android:layout_width="match_parent"
16        android:layout_height="match_parent"
17        android:orientation="vertical">

18
19        <TextView
20            android:id="@+id/greetingTextView"
21            android:layout_width="wrap_content"
22            android:layout_height="wrap_content"
23            android:text="@{greetingGeneratorObj.baseText}" />

24
25        <Button
26            android:id="@+id/helloButton"
27            android:layout_width="wrap_content"
28            android:layout_height="wrap_content"
29            android:onClick="@{handlers.onClickHello}"
30            android:text="hello" />

31
32        <Button
33            android:id="@+id/goodbyeButtonClicked"
34            android:layout_width="wrap_content"
35            android:layout_height="wrap_content"
36            android:onClick="@{handlers.onClickGoodbye}"
37            android:text="Good bye" />

38    </LinearLayout>
39</layout>



在MVVM模式中,本demo有四个类:除了MvvmMainActivity和GreetingGeneratorModel外,新增了GreetingGeneratorObj数据对象类和MyHandlers绑定方法类。


在MvvmMainActivity中,通过ActivityMvvmmainBinding对象的setHandlers方法进行事件绑定,可以看到,setOnClickListener代码消失了;

通过ActivityMvvmmainBinding对象的setGreetingGeneratorObj方法进行数据绑定。具体双向数据绑定是怎么执行的,都由DataBinding框架来完成。


 1public class MvvmMainActivity extends AppCompatActivity {
2
3    @Override
4    protected void onCreate(Bundle savedInstanceState) {
5        super.onCreate(savedInstanceState);
6        ActivityMvvmmainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_mvvmmain);
7        GreetingGeneratorObj greetingGeneratorObj = new GreetingGeneratorObj("");
8        // (1)按套路写好数据绑定,具体怎么实现双向数据绑定,都由DataBinding框架来完成
9        // 数据绑定
10        binding.setGreetingGeneratorObj(greetingGeneratorObj);
11        // 事件绑定
12        binding.setHandlers(new MyHandlers(greetingGeneratorObj));
13    }
14
15}


 1// Model层,封装各种数据来源
2public class GreetingGeneratorModel extends AsyncTask<VoidVoidInteger{
3    private String baseText;
4    private GreetingGeneratorObj greetingGeneratorObj;
5
6    public GreetingGeneratorModel(String baseText, GreetingGeneratorObj greetingGeneratorObj) {
7        this.baseText = baseText;
8        this.greetingGeneratorObj = greetingGeneratorObj;
9    }
10
11    // Simulates computing and returns a random integer
12    @Override
13    protected Integer doInBackground(Void... params) {
14        try {
15            // Simulate computing
16            Thread.sleep(2000);
17        } catch (InterruptedException e) {
18        }
19
20        return (int) (Math.random() * 100);
21    }
22
23    @Override
24    protected void onPostExecute(Integer randomInt) {
25        // (3)ViewModel层调用ViewModel层
26        greetingGeneratorObj.setBaseText(baseText + randomInt);
27    }
28}


数据对象的set方法需要增加notifyPropertyChanged方法。


 1// 数据对象类
2public class GreetingGeneratorObj extends BaseObservable {
3    public static String baseText;
4
5    public GreetingGeneratorObj(String baseText) {
6        this.baseText = baseText;
7    }
8
9    @Bindable
10    public String getBaseText() {
11        return baseText;
12    }
13
14    public void setBaseText(String baseText) {
15        GreetingGeneratorObj.baseText = baseText;
16        notifyPropertyChanged(BR.baseText);
17    }
18}


MyHandlers中实现操作Model的方法,这些方法与xml中布局文件是对应的,会被DataBinding框架自动调用。


 1public class MyHandlers {
2    private GreetingGeneratorObj greetingGeneratorObj;
3
4    public MyHandlers(GreetingGeneratorObj greetingGeneratorObj) {
5        this.greetingGeneratorObj = greetingGeneratorObj;
6    }
7
8    public void onClickHello(View view) {
9        // (2)ViewModel层调用Model层
10        new GreetingGeneratorModel("HelloWorld", greetingGeneratorObj).execute();
11    }
12
13    public void onClickGoodbye(View view) {
14        // (2)ViewModel层调用Model层
15        new GreetingGeneratorModel("GoodBye", greetingGeneratorObj).execute();
16    }
17}


04

https://github.com/youjinjin/ArchitecturalPatternDemo

以上是关于三种架构模式——MVCMVPMVVM的主要内容,如果未能解决你的问题,请参考以下文章

MVC/MVVM模式特点及区别

「数据仓库架构」数据仓库的三种模式建模技术

「微服务架构」跨多个微服务的数据架构模式

浅谈Java Web经典三层架构和MVC框架模式

三种架构模式——MVCMVPMVVM

还在搞三层架构?了解下 DDD 分层架构的三种模式吧