IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 手写Android中MVC、MVP、MVVM对比 -> 正文阅读

[移动开发]手写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");
        }
    }
}

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-03-10 22:41:16  更:2022-03-10 22:41:30 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 16:43:17-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码