【Android】浅析MVC与MVP

devtools/2024/9/22 17:46:51/

【Android】浅析MVC与MVP

文章目录

  • 【Android】浅析MVC与MVP
  • 什么是架构?
  • MVC架构
    • Model-View-Controller
    • Model
    • View
    • Controller
    • 解决什么问题
    • 数据的流向
      • MVC 模式的工作流程
    • MVC 架构模式的优缺点
  • MVP架构
    • Model-View-Presenter
    • 解决什么问题
    • 数据流向
      • MVC 和 MVP 的核心区别:角色通信
      • MVP 中的数据流向
    • MVP 架构模式的优缺点
    • MVP的绑定过程
      • 接口
      • 接口在MVP中的作用
      • 依赖注入
        • 依赖注入的方式
          • 1. 构造函数注入
          • 2. 方法注入
  • 结语

什么是架构?

架构(Architecture)在软件开发中指的是软件系统的整体设计和结构,它描述了系统的高层组织方式,包括系统中各个组件之间的关系、依赖、交互方式,以及这些组件如何协同工作来完成系统的功能。

架构不仅仅是指代码的结构,它还涵盖了系统的各个方面,包括:

  1. 组件的划分:将系统拆分成模块、类、服务等单元。
  2. 组件间的交互:不同模块、组件如何传递信息、调用服务等。
  3. 数据流:数据如何在系统中流动,从输入到输出的路径是什么。
  4. 非功能性需求:如何考虑系统的性能、可扩展性、安全性、可维护性等。

架构模式,其实更多的是一种思想,一种规则,往往一种架构模式可能会有不同的实现方式,而实现方式之间,只有合适与否,并没有对错之分

  1. 为了解决特定的问题而提出
  2. 按照特定的原则将系统整体进行模块/组件/角色的划分
  3. 建立模块/组件/角色间的沟通机制

摘自:https://zhuanlan.zhihu.com/p/83635530

MVC架构

在不同的框架或平台上,MVC(Model-View-Controller)架构的具体实现方式会有所不同,但其核心思想是不变的:将数据、逻辑与用户界面分离,以提高应用程序的可维护性、可扩展性和灵活性。

Model-View-Controller

MVC 将应用程序分为三个主要的部分:

Model

Model 负责应用程序的业务逻辑和数据处理,它代表了应用中的数据和状态。所有与数据相关的操作(如网络请求、数据库操作等)都在Model中进行。

职责:管理应用的数据,包括获取数据、存储数据、处理数据、数据验证等。

在Android中的体现

  • 可以是任何与数据处理相关的类,比如网络请求类、数据库操作类等。
  • 例如:通过Room数据库、SQLite数据库、或者API接口获取数据。

View

View 负责显示数据,是应用程序用户界面(UI)的表现部分。View直接与用户交互,展示Model中的数据并将用户操作传递给Controller进行处理。

职责:展示Model中的数据,并响应用户的交互操作(如点击按钮、输入文本等)。

在Android中的体现

  • XML布局文件(Layout)或Java中的UI元素(如TextViewButton等)。
  • Fragment或Activity中的onCreate()方法中设置布局和更新UI的代码。
  • View本身不应该包含业务逻辑,只负责显示数据和响应事件。

Controller

Controller 负责协调Model和View之间的交互,通常处理用户输入并将这些输入传递给Model,之后将Model处理的数据结果反馈给View。

职责:接收用户操作的输入,调用Model更新数据,并通知View来更新UI。

在Android中的体现

  • Activity和Fragment通常充当Controller的角色,因为它们负责管理UI逻辑和用户交互。
  • 在Activity或Fragment中,通过监听用户的交互事件(如点击事件)来修改Model,然后更新View。

image-20240920232739357

解决什么问题

如果不使用架构进行开发,会导致 Activity / Fragment 逻辑臃肿,不利于扩展。

所以 MVC 就要解决的问题就是:控制逻辑,数据处理逻辑和界面交互耦合

数据的流向

MVC 模式的工作流程

  1. 用户操作 View:用户通过UI进行某些操作(如点击按钮、输入文本等),这些操作通过事件监听传递到Controller。
  2. Controller 更新 Model:Controller接收用户的操作,并根据操作调用Model来获取或更新数据(比如向API发送请求,或者从数据库中获取数据)。
  3. Model 变更通知:Model处理完数据后,将更新后的数据通知给Controller或直接通知View。
  4. View 更新显示:Controller根据Model的数据变化来更新View,从而将新数据展示给用户。

在传统的 MVC 模式中,View 和 Model 可以直接通信。也就是说,View 可以直接读取 Model 的数据,而不需要通过 Controller。这种直接通信导致 ViewModel 之间的耦合度较高,尤其在复杂应用中,难以维护和测试。

MVC 架构模式的优缺点

优点:

  1. 结构清晰,职责划分清晰
  2. 降低耦合
  3. 有利于组件重用

缺点:

  1. 其实我们上述的示例,已经是经过优化的 MVC 结构了,Activity / Fragment 会承担 View 和 Controller 两个角色,就会导致 Activity / Fragment 中代码较多
  2. Model 直接操作 View,View 的修改会导致 Controller 和 Model 都进行改动
  3. 增加了代码结构的复杂性

虽然MVC是一个经典的设计模式,但在Android开发中,由于Activity和Fragment承担了太多的角色(既作为Controller,又直接操作View),会导致代码臃肿,维护困难。因此,Android开发中更多地使用MVP(Model-View-Presenter)或MVVM(Model-View-ViewModel)架构模式,来更好地解耦UI和业务逻辑。

MVP架构

MVP(Model-View-Presenter)是Android开发中的一种架构模式,旨在通过更清晰地分离职责,解决MVC中Activity和Fragment过度承担的角色,使代码更加模块化、可维护性更高。MVP将逻辑代码与UI代码解耦,避免了UI组件直接与业务逻辑耦合。MVP是对MVC模式的改进,非常适合中小型Android应用。

image-20240920232902424

Model-View-Presenter

  1. Model(模型) Model在MVP中负责数据的处理和业务逻辑,跟MVC中的Model角色一致。它与Presenter通信,提供数据和执行相应的业务逻辑。
  2. View(视图) View负责展示UI,与用户进行交互。它只处理用户输入和UI更新,不包含业务逻辑。在MVP中,View通过接口与Presenter通信,将事件交由Presenter处理,而不是自己操作业务逻辑。
  3. Presenter(控制器) Presenter是MVP的核心,它作为Model和View之间的中介,负责从Model获取数据并通知View进行更新。Presenter中不应该包含任何UI代码,它只处理逻辑和决定如何将数据传递给View。

解决什么问题

MVP 要解决的问题和 MVC 大同小异:控制逻辑,数据处理逻辑和界面交互耦合,同时能将 MVC 中的 View 和 Model 解耦

数据流向

MVC 和 MVP 的核心区别:角色通信

MVC 中,ViewModel 直接交互。这意味着 View 可能直接访问 Model 来获取数据,也可以监听 Model 的变化并更新界面。Controller 作为用户输入的处理者,接收用户输入并将其转发给 ModelView。这种直接交互可能导致ViewModel之间的耦合度较高。

MVP 则通过 Presenter 作为中介,解耦了 View 和 Model 之间的直接通信View 不会直接访问 Model,所有的数据交互、逻辑处理、UI 更新都通过 Presenter 进行。View 只负责呈现数据,而不涉及任何业务逻辑。Presenter负责处理业务逻辑,并且通过接口与ViewModel交互,从而实现低耦合。

MVP 中的数据流向

MVP 模式下的数据流向可以总结为以下几个步骤:

用户事件触发(View -> Presenter)

用户在 UI 界面(View)上执行操作(如点击按钮、输入文本等)。View 接收到用户的交互后,不直接处理逻辑,而是将事件传递给 Presenter

业务逻辑处理(Presenter -> Model)

Presenter 负责处理用户的输入,并做出相应的业务逻辑处理。如果需要修改数据或与后端进行交互,Presenter 会通过接口调用 Model 的方法,来处理业务逻辑或更新数据。

数据更新(Model -> Presenter)

Model 根据 Presenter 的指令更新数据或获取数据。当 Model 处理完数据之后,它会将结果返回给 Presenter

界面更新(Presenter -> View)

Presenter 收到来自 Model 的新数据后,将这些数据传递给 View,从而驱动 UI 界面的更新。View 只负责根据 Presenter 提供的数据更新界面,不会进行数据处理或逻辑处理。

举个栗子吧~:

以一个简单的登录流程为例:

用户输入用户名和密码,点击登录按钮

用户在 View 中(例如 LoginActivity)输入用户名和密码,并点击“登录”按钮。View 不直接处理输入,而是将事件通知给 Presenter。例如:presenter.onLoginButtonClick(username, password)

Presenter 接收用户输入,开始验证逻辑

Presenter 接收到用户输入后,执行登录验证逻辑。Presenter 通过调用 Model 的方法(例如 model.login(username, password))来进行登录操作。

Model 处理登录逻辑

Model 负责处理登录的具体业务逻辑,例如查询数据库或通过网络请求验证用户名和密码。验证成功后,Model 将结果返回给 Presenter(例如成功或失败的状态)。

Presenter 获取验证结果,并通知 View 更新界面

Presenter 接收到登录的结果后,决定如何通知 View 更新界面。如果登录成功,Presenter 可能调用 View 的方法来显示“登录成功”的消息;如果失败,则显示“登录失败”的提示。

MVP 架构模式的优缺点

优点:

  1. 结构清晰,职责划分清晰
  2. 模块间充分解耦
  3. 有利于组件的重用

缺点:

  1. 会引入大量的接口,导致项目文件数量激增
  2. 增大代码结构复杂性

MVP的绑定过程

上面提到了一个词非常眼熟,叫解耦

似乎陌生又熟悉,我们来复习一下耦合度的概念。

高内聚,低耦合:类的内部元素(方法、属性等)应该紧密相关,而与外部的类之间的依赖关系应该尽量降低

这个概念大家一定不陌生,但MVP是如何实现解耦的呢,解耦需要用到什么样的技术和方法呢?

接口

接口在MVP中的作用

  1. 定义View与Presenter的交互契约 接口定义了View和Presenter之间的交互规则,使得两者之间通过接口通信,而不直接依赖彼此的实现。这样,View和Presenter可以独立演进或测试,而不影响其他部分。

  2. 解耦View与Presenter 在MVP架构中,View和Presenter通过接口进行交互,而不是直接依赖具体的类。这样一来,View(例如Activity或Fragment)可以很容易地替换,而无需更改Presenter的实现,反之亦然。

依赖注入

依赖注入(Dependency Injection, DI)是软件开发中的一种设计模式,通过将对象的依赖关系从内部创建转变为外部传递,达到解耦的目的。依赖注入可以减少模块之间的直接依赖,使代码更加灵活、可维护,并且方便测试。

  1. 依赖:一个对象所需要的另一个对象。例如,Car类依赖于Engine类,因为汽车需要发动机来工作。
  2. 注入:将依赖传递给需要它的类,而不是类自己创建或查找依赖。这可以通过构造函数、方法、或属性注入来实现。
依赖注入的方式

依赖注入可以通过三种方式来实现:构造函数注入方法注入属性注入

1. 构造函数注入

这是最常用的依赖注入方式,依赖对象通过类的构造函数传递。构造函数注入能确保对象在创建时就获得了所需的依赖。

// Car类依赖于Engine类
public class Car {private Engine engine;// 通过构造函数注入依赖public Car(Engine engine) {this.engine = engine;}public void drive() {engine.start();System.out.println("Car is driving...");}
}// Engine类
public class Engine {public void start() {System.out.println("Engine started...");}
}// Main类
public class Main {public static void main(String[] args) {// 外部创建依赖对象EngineEngine engine = new Engine();// 通过构造函数将依赖注入给CarCar car = new Car(engine);// 调用方法car.drive();}
}

在这个例子中,Car类依赖于Engine类,但是Car没有直接创建Engine实例,而是通过构造函数接收它。这实现了依赖注入,使Car类与Engine类的实现细节解耦。

2. 方法注入

依赖对象可以通过类的某个方法传递。与构造函数注入不同,方法注入是在对象被创建之后调用某个方法来设置依赖。

public class Car {private Engine engine;// 通过方法注入依赖public void setEngine(Engine engine) {this.engine = engine;}public void drive() {engine.start();System.out.println("Car is driving...");}
}public class Main {public static void main(String[] args) {Engine engine = new Engine();Car car = new Car();// 通过方法注入依赖car.setEngine(engine);car.drive();}
}

方法注入的灵活性更高,依赖可以在对象创建之后动态注入。不过,它不如构造函数注入安全,因为对象可能在依赖未注入之前就被使用。

笔者写完了自己的MVP才发现需要依赖注入,警钟长鸣。

我们来看看(参考学长的代码)MVP当中是用什么来实现依赖注入的:

代码参考自:

【Android】MVC与MVP的区别,MVP网络请求实践_android mvc和mvp的区别-CSDN博客

image-20240922162745500

首先创建task,也就是model层的实例,这里采用调用方法的形式创建一个单例的model。

单例的实现方式:

image-20240922163223004

而后先将创建好的view和model注入presenter之中,而后调用方法将presenter中注入view之中。

image-20240922163653232

通过依赖注入,类之间的依赖关系通过外部进行配置或注入,从而将依赖与类的业务逻辑分离,降低了类之间的耦合度。

结语

本文仅仅对MVP和MVC做了一个小的总结,希望在日后可以学习更多有关架构模式的知识,例如:依赖注入、响应式编程、MVVM等。

参考:

Android 开发中的架构模式 – MVC / MVP / MVVM - 知乎 (zhihu.com)


http://www.ppmy.cn/devtools/115574.html

相关文章

Hive企业级调优[6]——HQL语法优化之任务并行度

目录 HQL语法优化之任务并行度 优化说明 Map端并行度 Reduce端并行度 优化案例 HQL语法优化之任务并行度 优化说明 对于分布式计算任务来说,设置一个合理的并行度至关重要。Hive的计算任务依赖于MapReduce框架来完成,因此并行度的调整需要从Map端和…

mac新手入门(快捷键)

系统常用快捷键 基本操作 Command-Z 撤销Shift-Command-Z:重做最近的撤销操作Command-X 剪切  Command-C 拷贝(Copy) Option Shift Command V 纯文本拷贝 Command-V 粘贴  Command-A 全选(All)Command-S 保…

大数据Flink(一百二十一):Flink CDC基本介绍

文章目录 Flink CDC基本介绍 一、什么是CDC 二、CDC的实现机制 三、​​​​​​​​​​​​​​传统 CDC ETL 分析 四、​​​​​​​​​​​​​​基于 Flink CDC 的 ETL 分析 五、​​​​​​​​​​​​​​什么是 Flink CDC 六、​​​​​​​​​​​​​​…

【数据结构】顺序表和链表经典题目

系列文章目录 单链表 动态顺序表实现通讯录 顺序表 文章目录 系列文章目录前言一、顺序表经典例题1. 移除元素2. 合并两个有序数组 二、链表经典例题1. 移除链表元素2. 反转链表3. 合并两个有序链表4. 链表的中间节点5. 环形链表的约瑟夫问题 总结 前言 我们通过前面对顺序表…

react + antDesignPro 企业微信扫码登录

效果 实现步骤 1、项目中document.ejs文件引入企微js链接 注意&#xff1a;技术栈是使用的react antDesignPro&#xff0c;不同的技术栈有不同的入口文件&#xff08;如vue在html文件引入&#xff09; <script src"https://wwcdn.weixin.qq.com/node/wework/wwopen/j…

进程间关系与进程守护

一、进程组 1、理解 每一个进程除了有一个进程 ID(PID)之外 还属于一个进程组&#xff0c; 进程组是一个或者多个进程的集合&#xff0c; 一个进程组可以包含多个进程。 每一个进程组也有一个唯一的进程组 ID(PGID)&#xff0c; 并且这个 PGID 类似于进程 ID&#xff0c; 同样…

pytorch 显存分配机制

pytorch 显存分配机制 pyTorch 的显存分配机制旨在高效利用 GPU 的显存&#xff0c;并减少不必要的显存分配和释放操作&#xff0c;从而提高模型训练和推理的性能。以下是 PyTorch 在使用 CUDA 进行显存分配和管理时的一些主要机制和特点&#xff1a; 1. 显存管理的基础 PyT…

【Linux取经之路】Linux项目自动化构建工具-make/makefile git三板斧

目录 关于make和makefile 一个案例 make和makefile的使用 makefile的基本语法 git的使用 关于make和makefile make是 Linux 系统中广泛使用的一个自动化构建工具&#xff0c;它根据用户定义的规则&#xff08;通常保存在一个名为 makefile的文件中&#xff09;来自动编译…