手写Android中MVC、MVP、MVVM对比

1. MVC、MVP、MVVM

Android中MVC、MVP、MVVM述

1.1 MVC

  • Model 模型层: 业务模型的数据与行为=数据+业务逻辑
  • View 展示层: 管理用户界面=组合模式的View集合
  • Controller: Model与View的桥梁,用于控制程序流程, 确保View可以访问和显示Model的数据。Android中为生命周期和事件机制收发,例如Activity、Fragment
  • 优点:开创性提出了View与Model分离
  • 缺点:存在View和Controller没有彻底解耦和Controller承担了过重的非本职任务
  • 解耦原理:通过Activity实现View和Model的解耦

1.2 MVP

  • Model 模型层: 业务模型的数据与行为=数据+业务逻辑
  • View 展示层: 管理用户界面=组合模式的View集合
  • Presenter:负责View和Model的沟通即具体业务逻辑
  • 优点:完全解耦
  • 缺点:项目越复杂,接口定义越多,导致开发难度新增
  • 解耦原理:通过接口编程的方式实现View和Model分离

1.3 MVVM

  • Model 模型层: 业务模型的数据与行为=数据+业务逻辑
  • View 展示层: 管理用户界面=组合模式的View集合
  • ViewModel: 业务逻辑与数据,ViewModel与View绑定之后,ViewModel变化会通知View更新变化
  • 优点:MV完全解耦,适合大型项目
  • 缺点:需要依赖Jetpack实现
  • 解耦原理:Jetpack框架实现,implementation ‘android.arch.lifecycle:extensions:1.1.1’

2. MVC、MVP、MVVM Demo

用3种写法实现:登录界面功能

2.1 Model和View层

2.1.1 Model
package com.sufadi.study.mvx;

import android.util.Log;

/**
 * Model层
 * Model 模型层: 业务模型的数据与行为=数据+业务逻辑
 */
public class ModelUser {
    String name;
    String pwd;

    public ModelUser() {
        Log.d("ModelUser", "Model 模型层实例化");
    }

    String getInfo(String name) {
        if ("fadi.su".equals(name)) {
            return "从数据库查询到,该用户永远18岁";
        } else {
            return "保密";
        }
    }
}

2.1.2 View

简单的登录界面:2个输入框分别输入用户名和密码和1个登录按钮
setContentView(R.layout.login_layout);

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/title_tv"
        android:text="MVC demo"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <TextView
            android:text="用户名称:"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <EditText
            android:id="@+id/name_et"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="fadi.su"/>
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <TextView
            android:text="用户密码:"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <EditText
            android:id="@+id/pwd_et"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="123"/>
    </LinearLayout>

    <Button
        android:id="@+id/login_btn"
        android:text="登录"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</LinearLayout>

2.2 MVC

  • Model 模型层: 业务模型的数据与行为=数据+业务逻辑
  • View 展示层: 管理用户界面=组合模式的View集合
  • Controller: Model与View的桥梁,用于控制程序流程, 确保View可以访问和显示Model的数据。Android中为生命周期和事件机制收发,例如Activity、Fragment
  • 优点:开创性提出了View与Model分离
  • 缺点:存在View和Controller没有彻底解耦和Controller承担了过重的非本职任务

代码执行逻辑为:

2022-02-11 15:35:59.896 21467-21467/com.sufadi.study D/MVC: MVC
    Model 模型层: 业务模型的数据与行为=数据+业务逻辑
    View 展示层: 管理用户界面=组合模式的View集合
    Controller: Model与View的桥梁,用于控制程序流程, 确保View可以访问和显示Model的数据。Android中为生命周期和事件机制收发,例如Activity、Fragment
    优点:开创性提出了View与Model分离
    缺点:存在View和Controller没有彻底解耦和Controller承担了过重的非本职任务
2022-02-11 15:35:59.896 21467-21467/com.sufadi.study D/MVC: Controller 层:onCreate生命周期中findViewById()存在Controller层耦合了View层
2022-02-11 15:36:04.909 21467-21467/com.sufadi.study D/MVC: Controller 层:Activity主业是生命周期和事件机制外发,兼职验证用户名合法性
2022-02-11 15:36:04.909 21467-21467/com.sufadi.study D/MVC: Controller 层:Activity主业是生命周期和事件机制外发,兼职验证密码合法性
2022-02-11 15:36:04.910 21467-21467/com.sufadi.study D/MVC: Controller 层:Activity主业是生命周期和事件机制外发,兼职验证用户名密码是否正确
2022-02-11 15:36:04.910 21467-21467/com.sufadi.study D/ModelUser: Model 模型层实例化
2022-02-11 15:36:04.910 21467-21467/com.sufadi.study D/MVC: View 展示层:弹出一个对话框,提示登录成功:fadi.su, 从数据库查询到,该用户永远18岁

源码

MVCActivity.java

package com.sufadi.study.mvx;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import androidx.annotation.Nullable;

import com.sufadi.study.R;

/**
 * MVC
 *
 * adb shell am start -n com.sufadi.study/com.sufadi.study.mvx.MVCActivity
 */
public class MVCActivity extends Activity {

    public static String TAG = "MVC";

    // Controller 耦合了 View
    TextView title_tv;
    EditText name_et, pwd_et;
    Button login_btn;

    private static final String info = "\nMVC\n" +
            "Model 模型层: 业务模型的数据与行为=数据+业务逻辑\n" +
            "View 展示层: 管理用户界面=组合模式的View集合\n" +
            "Controller: Model与View的桥梁,用于控制程序流程, 确保View可以访问和显示Model的数据。Android中为生命周期和事件机制收发,例如Activity、Fragment\n" +
            "优点:开创性提出了View与Model分离\n" +
            "缺点:存在View和Controller没有彻底解耦和Controller承担了过重的非本职任务\n";


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.login_layout);
        Log.d(TAG, info);
        initView();
        initValue();
        initListener();
    }

    /**
     * Controller 耦合了 View
     */
    private void initView() {
        Log.d(TAG, "Controller 层:onCreate生命周期中findViewById()存在Controller层耦合了View层");
        title_tv = findViewById(R.id.title_tv);
        name_et = findViewById(R.id.name_et);
        pwd_et = findViewById(R.id.pwd_et);
        login_btn = findViewById(R.id.login_btn);
    }

    private void initValue() {
        title_tv.setText(info);
    }

    private void initListener() {
        login_btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // Controller做了View的工作
                Log.d(TAG, "Controller 层:Activity主业是生命周期和事件机制外发,兼职验证用户名合法性");
                String name = name_et.getText().toString();
                if ("".equals(name)) {
                    Log.d(TAG, "View 展示层:弹出一个对话框,提示用户名不能为空");
                    return;
                }

                // Controller做了View的工作
                Log.d(TAG, "Controller 层:Activity主业是生命周期和事件机制外发,兼职验证密码合法性");
                String pwd = pwd_et.getText().toString();
                if ("".equals(pwd)) {
                    Log.d(TAG, "View 展示层:弹出一个对话框,提示密码不能为空");
                    return;
                }

                verifyLogin(name, pwd, new LoginCallBack() { // Controller 工作:验证用户名密码是否正确
                    @Override
                    public void success(ModelUser user) {
                        // Controller做了View的工作
                        Log.d(TAG, "View 展示层:弹出一个对话框,提示登录成功:" + user.name + ", " + user.getInfo(user.name));
                    }

                    @Override
                    public void fail() {
                        // Controller做了View的工作
                        Log.d(TAG, "View 展示层:弹出一个对话框,提示登录失败");
                    }
                });

            }
        });
    }

    private interface LoginCallBack{
        void success(ModelUser user);
        void fail();
    }

    /**
     * Controller 工作:验证用户名密码是否正确
     */
    private void verifyLogin(String name, String pwd, LoginCallBack callBack) {
        Log.d(TAG, "Controller 层:Activity主业是生命周期和事件机制外发,兼职验证用户名密码是否正确");
        if ("fadi.su".equals(name) && "123".equals(pwd)) {
            ModelUser mModelUser = new ModelUser();
            mModelUser.name = name;
            callBack.success(mModelUser);
        } else {
            callBack.fail();
        }
    }
}

2.3 MVP

  • Model 模型层: 业务模型的数据与行为=数据+业务逻辑
  • View 展示层: 管理用户界面=组合模式的View集合
  • Presenter:负责View和Model的沟通即具体业务逻辑
  • 优点:完全解耦
  • 缺点:项目越复杂,接口定义越多,导致开发难度新增
  • 原理:通过接口编程的方式实现View和Model分离
2022-02-11 16:03:49.597 23295-23295/com.sufadi.study D/MVP: Model、View、Presenter都有各自接口定义各自的行为方法。通过接口编程达到View和Model的解耦。
2022-02-11 16:03:52.869 23295-23295/com.sufadi.study D/Presenter: Presenter 层:执行用户名和密码的合法性和正确性流程
2022-02-11 16:03:52.869 23295-23295/com.sufadi.study D/ModelUser: Model 模型层实例化
2022-02-11 16:03:52.869 23295-23295/com.sufadi.study D/Presenter: Presenter 层:回调接口-success
2022-02-11 16:03:52.869 23295-23295/com.sufadi.study D/MVP: View 展示层:弹出一个对话框,提示登录成功:fadi.su, 从数据库查询到,该用户永远18岁
源码

MVPActivity.java

package com.sufadi.study.mvx;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import androidx.annotation.Nullable;

import com.sufadi.study.R;

/**
 * MVP
 * Model 模型层: 业务模型的数据与行为=数据+业务逻辑
 * View 展示层: 管理用户界面=组合模式的View集合
 * Presenter:负责View和Model的沟通即具体业务逻辑
 * 优点:完全解耦
 * 缺点:项目越复杂,接口定义越多,导致开发难度新增
 * 通过接口编程的方式实现View和Model分离。
 *
 * adb shell am start -n com.sufadi.study/com.sufadi.study.mvx.MVPActivity
 */
public class MVPActivity extends Activity implements IViewLogin {

    public static String TAG = "MVP";

    TextView title_tv;
    EditText name_et, pwd_et;
    Button login_btn;

    PresenterLogin presenterLogin;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "Model、View、Presenter都有各自接口定义各自的行为方法。通过接口编程达到View和Model的解耦。");
        setContentView(R.layout.login_layout);
        initView();
        initValue();
        initListener();
    }


    private void initView() {
        title_tv = findViewById(R.id.title_tv);
        name_et = findViewById(R.id.name_et);
        pwd_et = findViewById(R.id.pwd_et);
        login_btn = findViewById(R.id.login_btn);
    }

    private void initValue() {
        title_tv.setText(TAG);
        presenterLogin = new PresenterLogin(this);
    }

    private void initListener() {
        login_btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                presenterLogin.login(name_et.getText().toString(), pwd_et.getText().toString());
            }
        });
    }


    @Override
    public void success(ModelUser user) {
        Log.d(TAG, "View 展示层:弹出一个对话框,提示登录成功:" + user.name + ", " + user.getInfo(user.name));
    }

    @Override
    public void fail() {
        Log.d(TAG, "View 展示层:弹出一个对话框,提示登录失败");
    }

    @Override
    public void invalidName() {
        Log.d(TAG, "View 展示层:弹出一个对话框,提示用户名不能为空");
    }

    @Override
    public void invalidPwd() {
        Log.d(TAG, "View 展示层:弹出一个对话框,提示密码不能为空");
    }

}

PresenterLogin.java

package com.sufadi.study.mvx;

import android.util.Log;

public class PresenterLogin {

    private IViewLogin viewLogin;

    public PresenterLogin(IViewLogin _viewLogin) {
        this.viewLogin = _viewLogin;
    }

    public void login(String name, String pwd) {
        Log.d("Presenter", "Presenter 层:执行用户名和密码的合法性和正确性流程");
        if ("".equals(name)) {
            Log.d("Presenter", "Presenter 层:回调接口-invalidName");
            viewLogin.invalidName();
            return;
        }

        if ("".equals(pwd)) {
            Log.d("Presenter", "Presenter 层:回调接口-invalidPwd");
            viewLogin.invalidPwd();
            return;
        }

        if ("fadi.su".equals(name) && "123".equals(pwd)) {
            ModelUser mModelUser = new ModelUser();
            mModelUser.name = name;
            Log.d("Presenter", "Presenter 层:回调接口-success");
            viewLogin.success(mModelUser);
        } else {
            Log.d("Presenter", "Presenter 层:回调接口-fail");
            viewLogin.fail();
        }
    }


}

IViewLogin.java

package com.sufadi.study.mvx;

/**
 * 通过接口编程的方式实现View和Model分离
 *
 * IView: 定义了哪些情况需要更新View
 */
public interface IViewLogin {
    void success(ModelUser name);
    void fail();
    void invalidName();
    void invalidPwd();
}

2.4 MVVM

  • Model 模型层: 业务模型的数据与行为=数据+业务逻辑
  • View 展示层: 管理用户界面=组合模式的View集合
  • ViewModel: 业务逻辑与数据,ViewModel与View绑定之后,ViewModel变化会通知View更新变化
  • 优点:MV完全解耦,适合大型项目
  • 缺点:需要依赖Jetpack实现
  • 解耦原理:Jetpack框架实现,implementation ‘android.arch.lifecycle:extensions:1.1.1’
2022-02-11 16:28:58.967 24637-24637/com.sufadi.study D/MVVM: MVVM
    Model 模型层: 业务模型的数据与行为=数据+业务逻辑
    View 展示层: 管理用户界面=组合模式的View集合
    ViewModel: 业务逻辑与数据,ViewModel与View绑定之后,ViewModel变化会通知View更新变化
    优点:MV完全解耦,适合大型项目
    缺点:需要依赖Jetpack实现
    解耦原理:Jetpack框架实现,implementation 'android.arch.lifecycle:extensions:1.1.1'
2022-02-11 16:28:59.272 24637-24637/com.sufadi.study D/ViewRootImpl[MVVMActivity]: reportDrawFinished
2022-02-11 16:29:02.705 24637-24637/com.sufadi.study D/ViewModel: ViewModel 层:执行用户名和密码的合法性和正确性流程
2022-02-11 16:29:02.705 24637-24637/com.sufadi.study D/ModelUser: Model 模型层实例化
2022-02-11 16:29:02.705 24637-24637/com.sufadi.study D/ViewModel: ViewModel 层:回调接口-success
2022-02-11 16:29:02.712 24637-24637/com.sufadi.study D/MVVM: View 展示层:弹出一个对话框,提示登录成功:fadi.su, 从数据库查询到,该用户永远18岁
源码
package com.sufadi.study.mvx;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProviders;

import com.sufadi.study.R;

/**
 * MVVM
 * Model 模型层: 业务模型的数据与行为=数据+业务逻辑
 * View 展示层: 管理用户界面=组合模式的View集合
 * ViewModel: 业务逻辑与数据,ViewModel与View绑定之后,ViewModel变化会通知View更新变化
 * 优点:MV完全解耦,适合大型项目
 * 缺点:需要依赖Jetpack实现
 * 解耦原理:Jetpack框架实现,implementation 'android.arch.lifecycle:extensions:1.1.1'
 *
 * adb shell am start -n com.sufadi.study/com.sufadi.study.mvx.MVVMActivity
 */
public class MVVMActivity extends AppCompatActivity {

    public static String TAG = "MVVM";

    TextView title_tv;
    EditText name_et, pwd_et;
    Button login_btn;

    private ViewModelLogin viewModelLogin;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "\nMVVM\n" +
                "Model 模型层: 业务模型的数据与行为=数据+业务逻辑\n" +
                "View 展示层: 管理用户界面=组合模式的View集合\n" +
                "ViewModel: 业务逻辑与数据,ViewModel与View绑定之后,ViewModel变化会通知View更新变化\n" +
                "优点:MV完全解耦,适合大型项目\n" +
                "缺点:需要依赖Jetpack实现\n" +
                "解耦原理:Jetpack框架实现,implementation 'android.arch.lifecycle:extensions:1.1.1'");
        setContentView(R.layout.login_layout);
        initView();
        initValue();
        initListener();
    }

    private void initView() {
        title_tv = findViewById(R.id.title_tv);
        name_et = findViewById(R.id.name_et);
        pwd_et = findViewById(R.id.pwd_et);
        login_btn = findViewById(R.id.login_btn);
    }

    private void initValue() {
        title_tv.setText(TAG);
        viewModelLogin = ViewModelProviders.of(this).get(ViewModelLogin.class);
    }

    private void initListener() {
        login_btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                viewModelLogin.login(name_et.getText().toString(), pwd_et.getText().toString());
            }
        });

        viewModelLogin.loginStatue.observe(this, new Observer<String>() {
            @Override
            public void onChanged(String statue) {
                if ("invalidName".equals(statue)) {
                    Log.d(TAG, "View 展示层:弹出一个对话框,提示用户名不能为空");
                } else if ("invalidPwd".equals(statue)) {
                    Log.d(TAG, "View 展示层:弹出一个对话框,提示密码不能为空");
                } else if("success".equals(statue)) {
                    ModelUser user = viewModelLogin.userMutableLiveData.getValue();
                    Log.d(TAG, "View 展示层:弹出一个对话框,提示登录成功:" + user.name + ", " + user.getInfo(user.name));
                } else {
                    Log.d(TAG, "View 展示层:弹出一个对话框,提示登录失败");
                }
            }
        });
    }
}

ViewModelLogin.java

package com.sufadi.study.mvx;

import android.util.Log;

import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

/**
 * Jetpack 提供ViewModel组件用于实现界面(Activity或Fragment)和数据分离
 */
public class ViewModelLogin extends ViewModel {

    public MutableLiveData<String> loginStatue = new MutableLiveData<>();
    public MutableLiveData<ModelUser> userMutableLiveData = new MutableLiveData<>();

    public void login(String name, String pwd) {
        Log.d("ViewModel", "ViewModel 层:执行用户名和密码的合法性和正确性流程");
        if ("".equals(name)) {
            Log.d("ViewModel", "ViewModel 层:回调接口-invalidName");
            loginStatue.postValue("invalidName");
            return;
        }

        if ("".equals(pwd)) {
            Log.d("ViewModel", "ViewModel 层:回调接口-invalidPwd");
            loginStatue.postValue("invalidPwd");
            return;
        }

        if ("fadi.su".equals(name) && "123".equals(pwd)) {
            ModelUser user = new ModelUser();
            user.name = name;
            userMutableLiveData.setValue(user);
            Log.d("ViewModel", "ViewModel 层:回调接口-success");
            loginStatue.postValue("success");
        } else {
            Log.d("ViewModel", "ViewModel 层:回调接口-fail");
            loginStatue.postValue("fail");
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

法迪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值