MVC和MVP的区别
2007年08月08日 星期三 上午 09:23

MVC和MVP到底有什么区别呢?

从这幅图可以看到,我们可以看到在MVC里,View是可以直接访问Model的!从而,View里会包含Model信息,不可避免的还要包括一些业务逻辑。

在MVC模型里,更关注的Model的不变,而同时有多个对Model的不同显示,及View。

所以,在MVC模型里,Model不依赖于View,但是View是依赖于Model的。不仅如此,因为有一些业务逻辑在View里实现了,导致要更改View也是比较困难的,至少那些业务逻辑是无法重用的。

Visual Studio等快速开发工具,让我们很难把View和Controller分开,我们总是直接在View的事件响应函数里完成了Controller的代码。而在ASP.NET和XAML里,使用了一种叫做Code-Behind的技术,可以把View和Controller进行分离。这样,View就可以完全由UI设计工程师来完成,而Controller由程序员来完成,两者可以直接合成不需要像现在一样再由程序员做很多的工作。

把Controller和View混在一起,有什么问题?

1.难以测试

  必须手动点击,使用各种自动化的测试工具。

2.代码难以重用。

  UI是很难重用,因为要求总是不同。所以,导致重复的代码四处都是,维护麻烦。

MVP是如何解决MVC的问题的?

在MVP里,Presenter完全把Model和View进行了分离,主要的程序逻辑在 Presenter里实现。而且,Presenter与具体的View是没有直接关联的,而是通过定义好的接口进行交互,从而使得在变更View时候可以 保持Presenter的不变,即重用!

不仅如此,我们还可以编写测试用的View(比如显示一个进度条,并不是真正显示进度条,只要把进度值打印出来观察即可,或者直接在测试view里略过ui的各种显示变化,只测试业务逻辑),模拟用户的各种操作,从而实现对Presenter的测试--而不需要使用自动化的测试工具。

我们甚至可以在Model和View都没有完成时候,就可以通过编写Mock Object(即实现了Model和View的接口,但没有具体的内容的)来测试Presenter的逻辑。

在MVP里,应用程序的逻辑主要在Presenter来实现,其中的View是很薄的一层。 因此就有人提出了Presenter First的设计模式,就是根据User Story来首先设计和开发Presenter。在这个过程中,View是很简单的,能够把信息显示清楚就可以了。在后面,根据需要再随便更改View, 而对Presenter没有任何的影响了。

如果要实现的UI比较复杂,而且相关的显示逻辑还跟Model有关系,就可以在View和 Presenter之间放置一个Adapter。由这个 Adapter来访问Model和View,避免两者之间的关联。而同时,因为Adapter实现了View的接口,从而可以保证与Presenter之 间接口的不变。这样就可以保证View和Presenter之间接口的简洁,又不失去UI的灵活性。

在MVP模式里,View只应该有简单的Set/Get的方法,用户用户输入和设置界面显示的内容,除此就不应该有更多的内容,绝不容许直接直接访问Model--这就是与MVC很大的不同之处。

mvp实现样例:

代码很简单就用登录的简单逻辑

图1 demo截图

Mvp代码实现

首先看下结构

  • 数据逻辑相当于M
  • Activity(负责View的绘制以及与用户交互)相当于V
  • View与Model间的交互则为P

图2 Mvp代码结构

从M开始

UserModel.Class

public class UserModel {

    private String username;
    private String password;

    public UserModel(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public int checkUserValidity(String username, String password) {
        if (username == null || password == null ||
                username.isEmpty() ||
                password.isEmpty()) {
            return -1;
        }
        return 0;
    }
}

接下来是V

ILoginView.Class

public interface ILoginView {

    void showProgress();

    void hideProgress();

    void setPasswordError();

    String getUsername();

    String getPassword();

    void loginSuccess();

}

LoginActivity.Class

public class LoginActivity extends AppCompatActivity implements ILoginView,View.OnClickListener{

    private EditText usernameEdit,passwrodEdit;

    private Button loginButton;

    ProgressDialog pd;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mvplogin);

        pd = new ProgressDialog(this);

        usernameEdit = (EditText) findViewById(R.id.et_username);
        passwrodEdit = (EditText) findViewById(R.id.et_username);
        loginButton = (Button) findViewById(R.id.bt_login);

        loginButton.setOnClickListener(this);

    }

    @Override
    public void showProgress() {
        pd.show();
    }

    @Override
    public void hideProgress() {
        pd.cancel();
    }

    @Override
    public void setPasswordError() {
        passwrodEdit.setError("passwrod error");
    }

    @Override
    public String getUsername() {
        return usernameEdit.getText().toString();
    }

    @Override
    public String getPassword() {
        return passwrodEdit.getText().toString();
    }

    @Override
    public void loginSuccess() {
        Toast.makeText(this, "login success", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.bt_login:

                break;
        }
    }

最后就是P了

ILoginPresenter.Class

public interface ILoginPresenter {

    void Login(String username, String password);

}

LoginPresenter.Class

public class LoginPersenter implements ILoginPresenter{

    private ILoginView loginView;
    private UserModel mUser;

    public LoginPersenter(ILoginView loginView) {
        this.loginView = loginView;
        initUser();
    }

    private void initUser(){
        mUser = new UserModel(loginView.getUsername(),loginView.getPassword());
    }

    @Override
    public void Login(String username, String password) {
        loginView.showProgress();
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                loginView.hideProgress();
                int code = mUser.checkUserValidity(loginView.getUsername(), loginView.getPassword());
                if (code == -1) {
                    loginView.setPasswordError();
                } else if (code == 0) {
                    loginView.loginSuccess();
                }
            }
        },2000);
    }

}

最后在LoginActivity中补上P的调用

//初始化
loginPresenter = new LoginPersenter(this);

//Click方法中的调用
loginPresenter.Login(usernameEdit.getText().toString(),passwrodEdit.getText().toString());

到这里MVP模式的代码就已经实现了

MVVM:

Mvvm代码实现

  • Model:数据模型层。包含业务逻辑和校验逻辑
  • View:屏幕上显示的UI界面(layout、views)
  • ViewModel:View和Model之间的链接桥梁,处理视图逻辑。

图3 Mvvm代码结构

从M开始

UserModel.Class

public class UserModel extends BaseObservable{

    private String username;
    private String password;

    public UserModel(String username, String password) {
        this.username = username;
        this.password = password;
    }

    @Bindable
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
        notifyPropertyChanged(BR.username);
    }

    @Bindable
    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
        notifyPropertyChanged(BR.password);
    }

    public int checkUserValidity() {
        if (username == null || password == null ||
                username.isEmpty() ||
                password.isEmpty()) {
            return -1;
        }
        return 0;
    }
}

接下来是V

activity_mvvmlogin.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="user"
            type="com.netease.mvpormvvmdemo.mvvm.UserModel"/>

    </data>

    <LinearLayout
        android:orientation="vertical" android:layout_width="match_parent"
        android:layout_height="match_parent">

        <EditText
            android:id="@+id/et_username"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <EditText
            android:id="@+id/et_password"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <Button
            android:id="@+id/bt_login"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Login"
            />

    </LinearLayout>

</layout>

最后是VM了

LoginActivity.Class

public class LoginActivity extends AppCompatActivity{

    ActivityMvvmloginBinding binding;
    ProgressDialog pd;
    UserModel userModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding = DataBindingUtil.setContentView(this, R.layout.activity_mvvmlogin);
        pd = new ProgressDialog(this);

        binding.btLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                userModel = new UserModel(binding.etUsername.getText().toString(),binding.etPassword.getText().toString());
                binding.setUser(userModel);
                doLoign();
            }
        });
    }

    private void doLoign(){
        pd.show();
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                pd.cancel();
                int code = userModel.checkUserValidity();
                if (code == -1) {
                    binding.etPassword.setError("passwrod error");
                } else if (code == 0) {
                    Toast.makeText(getBaseContext(), "login success", Toast.LENGTH_SHORT).show();
                }
            }
        },2000);
    }
}

到这里MVVM的代码也实现了

是不是看到这里觉得这不是很坑爹嘛?MVVM的写法和以前MVC的写法基本一样呀?这样还有什么意义?

确实一样,这是为什么呢?其实之前介绍的时候也有提到过Android中的Datebingding只能单向绑定,只能从ViewModel绑定到View中,所以呢View中数据的变化我们在ViewModel中并不能拿到,所以写法和MVC没有什么区别

但是我们可以从ViewModel绑定到View中,这里其实就有很大的变化了

那我们修改一下登陆逻辑,登录后清除界面元素信息,显示成功页或者失败页

MVP架构的代码就不书写了,和之前的是一样结构的,主要是来看下MVVM从ViewModel绑定到View中

首先修改下Model

public class UserModel extends BaseObservable{

    private String username;
    private String password;
    private int status = 1;

    @Bindable
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
        notifyPropertyChanged(BR.username);
    }

    @Bindable
    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
        notifyPropertyChanged(BR.password);
    }

    @Bindable
    public int getStatus() {
        return status;
    }

    public void setStatus(int status) {
        this.status = status;
        notifyPropertyChanged(BR.status);
    }

    public void checkUserValidity() {
        if (username == null || password == null ||
                username.isEmpty() ||
                password.isEmpty()) {
            setStatus(-1);
        }else {
            setStatus(0);
        }
    }
}

这里主要是增加了一个新的属性为status,这个属性主要是用作判断当前界面的状态

接下来看看view的修改

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <import type="android.view.View"/>

        <variable
            name="user"
            type="com.netease.mvpormvvmdemo.mvvm.UserModel"/>

    </data>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >

        <LinearLayout
            android:orientation="vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="@{user.status == 1 ? View.VISIBLE : View.GONE}"
            >

            <EditText
                android:id="@+id/et_username"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />

            <EditText
                android:id="@+id/et_password"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />

            <Button
                android:id="@+id/bt_login"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Login"
                />

        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            android:visibility="@{user.status == 0 ? View.VISIBLE : View.GONE}"
            >

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@{user.username}"
                />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@{user.password}"
                />

        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:visibility="@{user.status == -1 ? View.VISIBLE : View.GONE}"
            >

            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:text="error"
                />

        </LinearLayout>

    </RelativeLayout>

</layout>

view内主要是修改为三个布局,根据status的状态进行变化

最后看看viewmodel的变化,viewmodel其实没有怎么变化,因为model里面小的修改了下

private void doLoign(){
    pd.show();
    new Handler().postDelayed(new Runnable() {
        @Override
        public void run() {
            pd.cancel();
            userModel.checkUserValidity();
        }
    },2000);
}

这里的代码只有这些

当我们输入后点击login后,model会对输入状态取检测,从而改变model里面的status属性,而通过ViewModel,view会及时拿到最新的状态,从而通过判断去做view层的变化。这就是MVVM的绑定机制了

看下运行截图

图4 Mvvm

Android mvp模式、mvvm模式的更多相关文章

  1. 浅谈MVC、MVP、MVVM模式

    mvc的模式已经深入人心,想必大家都很熟悉,但是未必都能遵守mvc模式.我们的一个mvc项目比较简单,主要是数据库的查询.一个DBHelp类,封装了数据库的操作,然后Controller中进行中各种查 ...

  2. 转:界面之下:还原真实的 MVC、MVP、MVVM 模式

    前言 做客户端开发.前端开发对MVC.MVP.MVVM这些名词不了解也应该大致听过,都是为了解决图形界面应用程序复杂性管理问题而产生的应用架构模式.网上很多文章关于这方面的讨论比较杂乱,各种MV*模式 ...

  3. MVC、MVP、MVVM 模式对比

    MVC.MVP和MVVM这些开发模式为了分离视图(View)和模型(Model)而提出来的,直白说就是为了前后端分离. 1. MVC(Model View Controller)模式 MVC是比较直观 ...

  4. 设计模式之架构型MVC,MVP,MVVM模式

    一.MVCMVC,Model View Controller,是软件架构中最常见的一种设计模式,简单来说就是通过Controller的控制去操作Model层的数据,并且返回给view层展示.View跟 ...

  5. MVC、MVP、MVVM模式

    MVC,MVP和MVVM都是常见的软件架构设计模式(Architectural Pattern),它通过分离关注点来改进代码的组织方式.不同于设计模式(Design Pattern),只是为了解决一类 ...

  6. MVC,MVP 和 MVVM 模式如何选择?

    转摘:http://www.linuxidc.com/Linux/2015-10/124622.htm 前言 做客户端开发.前端开发对MVC.MVP.MVVM这些名词不了解也应该大致听过,都是为了解决 ...

  7. MVC、MVP、MVVM 模式

    一.前言 做客户端开发.前端开发对MVC.MVP.MVVM这些名词不了解也应该大致听过,都是为了解决图形界面应用程序复杂性管理问题而产生的应用架构模式.网上很多文章关于这方面的讨论比较杂乱,各种MV* ...

  8. MVC、MVP、MVVM模式的概念与区别

    1. MVC框架 MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑.数据.界面显示 ...

  9. 前端开发中的 MVC、MVP、MVVM 模式

    MVC,MVP和MVVM都是常见的软件架构设计模式(Architectural Pattern),它通过分离关注点来改进代码的组织方式.不同于设计模式(Design Pattern),只是为了解决一类 ...

  10. angular 实战系列 之 mvvm模式

    什么是MVVM模式 mvvm模式是mvc模式的一种变体,其中第一个m表示model,可以认为是数据对象的抽象,v代表view,vm代表view model ,是对view中的数据抽象(注1).mvvm ...

随机推荐

  1. cocos2d对动画的各种操作

    瞬时动作:瞬时动作的基类是InstantAction 1.放置位置   CGPoint p = ccp(width,height); [sprite runAction:[CCPlace action ...

  2. WEB-INF/views/menu/list.jsp (line: 26, column: 58) equal symbol expected

    根本原因是由于单引号和双引号的混乱使用导致的. 解决办法: 将双引号里面的双引号改成单引号: 单引号里面的双引号该成单引号. 我的问题好像又不是这样的,<c:forEach var=" ...

  3. EXTJS 5 开发环境搭建

    WEBstrom eclipse下载: http://www.eclipse.org/downloads/ spket 下载: 安装方式: http://wangke0611.iteye.com/bl ...

  4. JSon_零基础_004_将Set集合对象转换为JSon格式的对象字符串,返回给界面

    将Set集合对象转换为JSon格式的对象字符串,返回给界面 需要导入的jar包: 编写:servlet: package com.west.webcourse.servlet; import java ...

  5. (转)SQL对Xml字段的操作

    T-Sql操作Xml数据 一.前言 SQL Server 2005 引入了一种称为 XML 的本机数据类型.用户可以创建这样的表,它在关系列之外还有一个或多个 XML 类型的列:此外,还允许带有变量和 ...

  6. html随笔

    <!DOCTYPE HTML> <html> <head> <meta charset = "utf-8"> <script ...

  7. oracle的索引维护

    索引重建 Alter index idx_name rebuild partition index_partition_name [online nologging] 需要对每个分区索引做rebuil ...

  8. 夺命雷公狗---微信开发52----网页授权(oauth2.0)获取用户基本信息接口(2)

    我们在上一节课已经发送code给第三方了,那么这就要获取code去换取到用户的openid. 第一步:编写create_baseurl.php(上一节课程已经写完了) 第二步:编写vote1.php( ...

  9. SQL数据库之变量

    --学习SQL数据库,变量是必须要掌握的概念,系统变量就是变量中最重要的变量之一,下面是SQL中系统变量的应用实例 use AdventureWorksDW exec sp_addtype 'char ...

  10. C语言初学者代码中的常见错误与瑕疵(19)

    见:C语言初学者代码中的常见错误与瑕疵(19)