当前位置: 首页 > news >正文

深入理解MVP架构模式

引言

MVP(Model-View-Presenter,模型-视图-提供者)是一种广泛应用于软件开发中的架构模式,是经典MVC(Model-View-Controller)的变种。在传统的MVC模式中,Model和View之间存在直接的依赖和数据交互,这种设计在一定程度上会导致代码耦合性增加,影响系统的可维护性和可扩展性。而在MVP模式中,Model和View完全解耦,它们之间的交互通过Presenter来实现。这种设计提高了代码的可维护性和可测试性,降低了复杂度。

本文将深入探讨MVP的架构概念、体系结构,最后通过一个完整的登录功能示例,展示如何在实际项目中运用MVP架构。

架构概念

MVP架构的核心思想是通过引入一个Presenter作为中间层,解耦Model和View之间的直接依赖关系,使得View仅负责UI渲染和用户交互,而Presenter处理业务逻辑并与Model交互。在MVP模式下,Model和View不存在任何直接的依赖和交互。它们之间的通讯完全通过Presenter来实现,这样可以更好地分离职责,降低代码的耦合度,提高代码的清晰性和可测试性。

MVP模式的依赖和交互关系如下图所示。MVP中的各部分之间的通信都是双向的,Presenter作为中间者,居中协调View和Model的交互。
在这里插入图片描述
MVP架构的三层功能如下:

  • Model:负责处理数据的获取、存储和管理,通常包含数据访问层(如数据库、API等)和实体模型
  • View:负责展示UI并与用户交互。View是一个“被动视图”(Passive View),它不会包含任何业务逻辑,只接受Presenter的指令来更新界面。
  • Presenter:是MVP的核心组件,负责处理业务逻辑。Presenter从Model获取数据,并将其传递给View,同时监听View的用户操作并处理相应的逻辑。

体系结构

三层解耦

MVP通过引入Presenter层,彻底实现了Model、View和Presenter三层的解耦:

  • Model:专注于数据处理,不关心UI展示。
  • View:专注于UI展示,不关心数据的来源和逻辑处理。
  • Presenter:负责在View和Model之间传递信息和业务逻辑的处理。

与MVC相比,MVP最大的不同点在于View和Model之间没有直接交互,而是通过Presenter桥接。Presenter通过接口与View和Model通信,保证了各组件之间的低耦合性,从而使代码更易于维护和测试。

双向通信

在MVP中,Presenter是核心,View通过接口与Presenter通信,而Presenter又通过调用Model来处理数据,最后将处理结果返回给View。通信流程如下:

  • 用户在View中触发某个操作(如点击按钮)。
  • View将该操作通知给Presenter。
  • Presenter根据业务逻辑决定如何处理事件,并通过Model获取数据。
  • Model返回处理结果给Presenter。
  • Presenter将结果传递给View,更新UI。

MVP的典型通信流程如下图所示:

在这里插入图片描述

被动视图(Passive View)

MVP中的View是“被动视图”,它只负责接收用户的输入和展示UI,而不会主动处理数据或业务逻辑。所有的逻辑都由Presenter处理,这样设计让View变得更加简单,并且使Presenter更具测试性。Presenter通过接口与View交互,使得Presenter可以独立于具体的UI实现进行单元测试。

测试友好

MVP模式的另一个显著优势是它非常利于单元测试。Presenter包含了所有的业务逻辑,且通过接口与View和Model交互,因此可以轻松地使用Mock对象来隔离实际的View和Model,从而更高效地进行测试。与MVC不同,MVP的View只是被动更新UI,没有复杂的逻辑需要测试,Presenter和Model的逻辑则可以通过自动化测试进行充分验证。

完整示例:登录功能

为了更好地理解MVP架构,接下来通过一个简单的登录功能示例来展示MVP模式在实践中的应用。

定义接口

首先,为了保证Presenter与View和Model的解耦,我们需要为View和Model定义接口。

LoginView接口:

public interface LoginView {void showLoginSuccess(String message);void showLoginError(String error);void showLoading();void hideLoading();
}

LoginModel接口:

public interface LoginModel {void login(String username, String password, LoginCallback callback);interface LoginCallback {void onSuccess(String message);void onError(String error);}
}

实现Model和Presenter

LoginModel实现:

public class LoginModelImpl implements LoginModel {@Overridepublic void login(String username, String password, LoginCallback callback) {// 模拟网络请求if ("admin".equals(username) && "1234".equals(password)) {callback.onSuccess("Login Successful");} else {callback.onError("Invalid Credentials");}}
}

LoginPresenter实现:

public class LoginPresenter {private LoginView loginView;private LoginModel loginModel;public LoginPresenter(LoginView view) {this.loginView = view;this.loginModel = new LoginModelImpl(); // 可以通过依赖注入解耦}public void login(String username, String password) {loginView.showLoading();loginModel.login(username, password, new LoginModel.LoginCallback() {@Overridepublic void onSuccess(String message) {loginView.hideLoading();loginView.showLoginSuccess(message);}@Overridepublic void onError(String error) {loginView.hideLoading();loginView.showLoginError(error);}});}
}

View层实现

最后,我们在View层(如Activity或Fragment)中实现LoginView接口,并将操作委托给Presenter。

LoginActivity实现:

public class LoginActivity extends AppCompatActivity implements LoginView {private EditText usernameEditText;private EditText passwordEditText;private Button loginButton;private ProgressBar progressBar;private LoginPresenter loginPresenter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_login);// 初始化UI组件usernameEditText = findViewById(R.id.username);passwordEditText = findViewById(R.id.password);loginButton = findViewById(R.id.loginButton);progressBar = findViewById(R.id.progressBar);// 初始化PresenterloginPresenter = new LoginPresenter(this);// 设置按钮点击事件loginButton.setOnClickListener(v -> {String username = usernameEditText.getText().toString();String password = passwordEditText.getText().toString();loginPresenter.login(username, password);});}@Overridepublic void showLoginSuccess(String message) {Toast.makeText(this, message, Toast.LENGTH_SHORT).show();}@Overridepublic void showLoginError(String error) {Toast.makeText(this, error, Toast.LENGTH_SHORT).show();}@Overridepublic void showLoading() {progressBar.setVisibility(View.VISIBLE);}@Overridepublic void hideLoading() {progressBar.setVisibility(View.GONE);}
}

LoginActivity布局:

LoginActivity相对应activity_login.xml布局文件,它定义了简单的登录界面,包含用户名输入框、密码输入框、登录按钮和加载进度条。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:gravity="center"android:orientation="vertical"android:padding="16dp"><!-- 用户名输入框 --><EditTextandroid:id="@+id/username"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="Username"android:inputType="text"android:padding="12dp"android:layout_marginBottom="16dp"/><!-- 密码输入框 --><EditTextandroid:id="@+id/password"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="Password"android:inputType="textPassword"android:padding="12dp"android:layout_marginBottom="16dp"/><!-- 登录按钮 --><Buttonandroid:id="@+id/loginButton"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="Login"android:padding="12dp"android:layout_marginBottom="16dp"/><!-- 加载进度条 --><ProgressBarandroid:id="@+id/progressBar"android:layout_width="wrap_content"android:layout_height="wrap_content"android:visibility="gone"/></LinearLayout>

布局说明:

  • 用户名输入框 (EditText):用户输入登录用户名的地方,id为username。
  • 密码输入框 (EditText):用户输入登录密码的地方,id为password,并设置了输入类型为密码格式。
  • 登录按钮 (Button):用于触发登录操作的按钮,id为loginButton。
  • 加载进度条 (ProgressBar):在处理登录请求时显示的加载指示器,默认隐藏(visibility=“gone”),只有在请求处理中显示。

结论

MVP架构通过将View、Model、Presenter三层彻底解耦,使得业务逻辑、UI展示、数据处理分别在不同的层中负责。这样不仅提高了代码的可维护性和可测试性,也使得项目的扩展性更强。在实际开发中,MVP非常适用于复杂度较高的应用程序,尤其是在需要严格分离UI逻辑和业务逻辑的场景下

相关文章:

深入理解MVP架构模式

引言 MVP&#xff08;Model-View-Presenter&#xff0c;模型-视图-提供者&#xff09;是一种广泛应用于软件开发中的架构模式&#xff0c;是经典MVC&#xff08;Model-View-Controller&#xff09;的变种。在传统的MVC模式中&#xff0c;Model和View之间存在直接的依赖和数据交…...

Java面试题七

一、Java中的集合框架是如何组织的&#xff1f;列举几个常用的集合类。 Java中的集合框架是一个设计用来存储和操作对象集合的统一架构。它主要由两大接口派生出来&#xff1a;Collection和Map。这两个接口及其子接口和实现类共同构成了Java集合框架的主体。 集合框架的组织结…...

linux网络编程3——http服务器的实现和性能测试

http服务器的实现 本文使用上一篇博文实现的epollreactor百万并发的服务器实现了一个使用http协议和WebSocket协议的WebServer。 完整代码请看我的github项目 1. 水平触发(Level Trigger)与边沿触发(Edge Trigger) 1.1 水平触发 水平触发是一种状态驱动机制。当文件描述符&a…...

Docker部署Kamailio,并使用LinPhone实现网络通话

前提条件 准备一个路由器&#xff0c;一个服务器&#xff0c;两个终端设备&#xff08;手机或电脑&#xff09; docker部署安装 我使用的是windows系统&#xff0c;docker desktop 先启动Docker desktop打开cmd&#xff0c;输入docker命令docker run --name kamailio --rm…...

JAVA-石头迷阵小游戏

采用企业式项目结构,接下来我将分享全部代码和结构,希望大家点点关注! 这是我的结构。首先使用IDE创建一个Module,命名stone-maze,接着把自带src下的main方法删除,接着在src下创建包,包名为com.wmuj,接着创建APP类代码如下: package com.wmuj;public class App {publ…...

鸿蒙--进度条通知

主要介绍如何使用通知能力和基础组件,实现模拟下载文件,发送通知的案例。 效果 代码结构 ├──entry/src/main/ets // 代码区 │ ├──common │ │ ├──constants │ │ │ └──CommonConstants.ets // 公共常量类 │ │ └──utils │ │ ├──Logger.ets //…...

搜维尔科技:varjo xr-4开箱测评,工业用途头显,一流视觉保真度

varjo xr-4开箱测评&#xff0c;工业用途头显&#xff0c;一流视觉保真度 搜维尔科技&#xff1a;varjo xr-4开箱测评&#xff0c;工业用途头显&#xff0c;一流视觉保真度...

mysql数据量分库分表

一、分库分表参考阈值 分库分表是解决大规模数据和高并发访问问题的常用策略。虽然没有绝对的阈值来决定何时进行分库分表&#xff0c;但以下是一些参考阈值和考虑因素&#xff0c;可以帮助你做出决策&#xff1a; 1.1 数据量阈值 单表数据行数&#xff1a;当单表的数据行数…...

Vite创建Vue3项目以及Vue3相关基础知识

1.创建Vue3项目 1.运行创建项目命令 # 使用 npm npm create vitelatest2、填写项目名称 3、选择前端框架 4、选择语法类型 5、按提示运行代码 不出意外的话&#xff0c;运行之后应该会出现 下边这个页面 6.延伸学习&#xff1a;对比webpack和vite&#xff08;这个是面试必考…...

Elasticsearch封装公共索引增删改查

什么是索引&#xff1f; 定义&#xff1a;索引是 Elasticsearch 中用于存储数据的逻辑命名空间。它由多个文档组成&#xff0c;每个文档是一个 JSON 格式的结构化数据对应关系&#xff1a;在关系数据库中&#xff0c;索引类似于表&#xff1b;而在 Elasticsearch 中&#xff0…...

Python异常检测:Isolation Forest与局部异常因子(LOF)详解

这里写目录标题 Python异常检测&#xff1a;Isolation Forest与局部异常因子&#xff08;LOF&#xff09;详解引言一、异常检测的基本原理1.1 什么是异常检测&#xff1f;1.2 异常检测的应用场景 二、Isolation Forest2.1 Isolation Forest的原理2.1.1 算法步骤 2.2 Python实现…...

Git的原理和使用(二)

1. git的版本回退 之前我们也提到过&#xff0c;Git 能够管理⽂件的历史版本&#xff0c;这也是版本控制器重要的能⼒。如果有⼀天你发现 之前前的⼯作做的出现了很⼤的问题&#xff0c;需要在某个特定的历史版本重新开始&#xff0c;这个时候&#xff0c;就需要版本 回退的功能…...

docker 发布镜像

如果要推广自己的软件&#xff0c;势必要自己制作 image 文件。 1 制作自己的 Docker 容器 基于 centos 镜像构建自己的 centos 镜像&#xff0c;可以在 centos 镜像基础上&#xff0c;安装相关的软件&#xff0c;之后进行构建新的镜像。 1.1 dockerfile 文件编写 首先&…...

投了15亿美元,芯片创新公司Ampere为何成了Oracle真爱?

【科技明说 &#xff5c; 科技热点关注】 一个数据库软件公司却想要操控一家芯片厂商&#xff0c;这样的想法不错。也真大胆。 目前&#xff0c;全球数据库巨头甲骨文Oracle已经持有Ampere Computing LLC 29%的股份&#xff0c;并有可能通过未来的投资选择权获得对这家芯片制造…...

vue 报告标题时间来自 elementUI的 el-date-picker 有开始时间和结束时间

要在Vue中使用 Element UI 的 el-date-picker 来选择开始时间和结束时间&#xff0c;并将其展示在报告中&#xff0c;以下是详细的实现步骤。 实现思路&#xff1a; 使用 Element UI 的 el-date-picker 组件&#xff0c;让用户选择时间范围&#xff08;开始时间和结束时间&am…...

简单几何问题的通解

来&#xff0c;这道题怎么做&#xff1f;边长为2的正方形内&#xff0c;2个扇形的交集面积是多少&#xff1f;这道题一定要画辅助线&#xff0c;因为要用到两个扇形的交点&#xff0c;如果不画辅助线&#xff0c;这个交点相关的4个子图一个都无法求出面积&#xff0c;只能求出子…...

DBeaver导出数据表结构和数据,导入到另一个环境数据库进行数据更新

在工作中&#xff0c;我们会进行不同环境之间数据库的数据更新&#xff0c;这里使用DBeaver导出新的数据表结构和数据&#xff0c;并执行脚本&#xff0c;覆盖另一个环境的数据库中对应数据表&#xff0c;完成数据表的更新。 一、导出 右键点击选中想要导出的数据表&#xff0…...

【Golang】合理运用泛型,简化开发流程

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…...

OpenCV单目相机内参标定C++

基于OpenCV 实现单目相机内参标定&#xff1a; a.使用OpenCV库实现内参标定过程。通过角点检测、亚像素角点定位、角点存储与三维坐标生成和摄像机标定分别获取左右相机的内参。 b.具体地&#xff0c;使用库函数检测两组图像&#xff08;左右相机拍摄图像&#xff09;中棋盘格…...

基于MATLAB(DCT DWT)

第三章 图像数字水印的方案 3.1 图像数字水印的技术方案 在数据库中存储在国际互联网上传输的水印图像一般会被压缩&#xff0c;有时达到很高的压缩比。因此&#xff0c;数字水印算法所面临的第一个考验就是压缩。JPEG和EZW&#xff08;Embedded Zero-Tree Wavelet&#xff0…...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制&#xff0c;因此这个了16进制的数据既可以翻译成为这个机器码&#xff0c;也可以翻译成为这个国标码&#xff0c;所以这个时候很容易会出现这个歧义的情况&#xff1b; 因此&#xff0c;我们的这个国…...

业务系统对接大模型的基础方案:架构设计与关键步骤

业务系统对接大模型&#xff1a;架构设计与关键步骤 在当今数字化转型的浪潮中&#xff0c;大语言模型&#xff08;LLM&#xff09;已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中&#xff0c;不仅可以优化用户体验&#xff0c;还能为业务决策提供…...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

前端导出带有合并单元格的列表

// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列&#xff0c;以便知晓哪些列包含有价值的数据&#xff0c;…...

智能AI电话机器人系统的识别能力现状与发展水平

一、引言 随着人工智能技术的飞速发展&#xff0c;AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术&#xff0c;在客户服务、营销推广、信息查询等领域发挥着越来越重要…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)

漏洞概览 漏洞名称&#xff1a;Apache Flink REST API 任意文件读取漏洞CVE编号&#xff1a;CVE-2020-17519CVSS评分&#xff1a;7.5影响版本&#xff1a;Apache Flink 1.11.0、1.11.1、1.11.2修复版本&#xff1a;≥ 1.11.3 或 ≥ 1.12.0漏洞类型&#xff1a;路径遍历&#x…...

虚拟电厂发展三大趋势:市场化、技术主导、车网互联

市场化&#xff1a;从政策驱动到多元盈利 政策全面赋能 2025年4月&#xff0c;国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》&#xff0c;首次明确虚拟电厂为“独立市场主体”&#xff0c;提出硬性目标&#xff1a;2027年全国调节能力≥2000万千瓦&#xff0…...

MySQL 8.0 事务全面讲解

以下是一个结合两次回答的 MySQL 8.0 事务全面讲解&#xff0c;涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容&#xff0c;并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念&#xff08;ACID&#xff09; 事务是…...

三分算法与DeepSeek辅助证明是单峰函数

前置 单峰函数有唯一的最大值&#xff0c;最大值左侧的数值严格单调递增&#xff0c;最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值&#xff0c;最小值左侧的数值严格单调递减&#xff0c;最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...