系统架构:MVVM

引言

MVVM 全称 Model-View-ViewModel,是在 MVP(Model-View-Presenter)架构模式基础上的进一步演进与优化。MVVM 与 MVP 的基本架构相似,但 MVVM 独特地引入了数据双向绑定机制。这一创新机制有效解决了 MVP 模式中 Model 与 View 之间存在的耦合问题,极大地简化了两者间的映射关系以及繁琐的 DOM 操作流程。MVVM 架构模式致力于助力开发者更高效地分离用户界面(UI)与复杂的业务逻辑,进而显著提升代码的可维护性与可扩展性。

MVVM架构范式最初由微软架构师Ken Cooper和Ted Peters提出,并在Windows Presentation Foundation(WPF)平台上成功实施。由另外一位微软架构师 John Gossman在其博客《Introduction to Model/View/ViewModel pattern for building WPF apps》中发布。

MVVM的组件

MVVM 主要由 Model、View、ViewModel 和 DataBinding 四个核心组件构成,其架构关系图示如下:
在这里插入图片描述

View

View组件即用户界面(User Interface),在MVVM架构中,View只包含一层很薄的展示逻辑。View通过数据绑定(DataBinding)与ViewModel进行交互。当View与ViewModel完成数据绑定后,View中的任何变化都会自动反馈到ViewModel中,而View则无法直接感知Model的存在。这种设计使得View更加专注于展示,而不需要处理复杂的业务逻辑。

Model

Model组件与MVP架构中的Model类似,负责数据的存储和管理。Model层提供数据更新接口供ViewModel调用,以实现数据的更新。当Model中的数据发生变化时,会通过通知机制(如Notify)告知ViewModel,以便ViewModel进行相应的处理。

ViewModel

ViewModel组件位于View和Model之间,负责将Model的数据转换成View可以感知的形式。ViewModel通过数据绑定(DataBinding)将数据与View进行绑定,使得数据的变化能够自动更新到View上,从而实现数据的双向绑定。此外,ViewModel还通常包含用户的交互逻辑,如处理按钮点击等事件。

DataBinding

数据绑定(DataBinding)是MVVM架构中实现View和ViewModel之间数据双向流动更新的核心机制。在MVC或MVP架构中,View的更新和响应需要开发者编写大量复杂且冗余的代码。而在MVVM架构中,由于数据绑定的存在,开发者无需编写这部分复杂的代码,可以将更多的精力投入到核心业务功能的开发上。

MVVM实现

MVVM 涵盖 View、ViewModel、Model、DataBinding 四个核心模块。一般而言,View 对应相应页面布局的 xml 文件;ViewModel 对应业务逻辑的实现;DataBinding 负责建立页面布局文件与 ViewModel 业务实现之间的绑定关系。在 Android 开发环境中,由于存在原生 DataBinding 库,我们往往难以一窥 DataBinding 实现的全貌。鉴于 C++ 本身缺乏类似 Android 的原生 DataBinding 库,此处我们依据 DataBinding 的观察者实现原理,编写一个 C++ 版本的用户登录 MVVM 示例,以此助力大家深入透彻地理解 MVVM 实现的精髓。

Observer接口

#include <vector>
#include <string>class Observer
{
public:virtual void onChanged(const std::string& fieldName) = 0;virtual ~Observer() = default;
};

ObservableField 类

ObservableField 类是数据绑定的核心要素,承担着存储数据以及在数据变更时通知所有观察者的关键职责。

template <typename T>
class ObservableField
{
public:explicit ObservableField(const std::string& fieldName, T value): m_fieldName(fieldName), m_value(std::move(value)){}void set(T value){if (m_value != value){m_value = std::move(value);notifyObservers();}}const T& get() const{return m_value;}void addObserver(Observer* observer){m_observers.push_back(observer);}private:void notifyObservers(){for (auto observer : m_observers){if (observer){observer->onChanged(m_fieldName);}}}std::string m_fieldName;T m_value;std::vector<Observer*> m_observers;
};

Model 类

UserModel 类用于验证用户名和密码。

#include <string>class UserModel
{
public:UserModel(std::string correctUsername, std::string correctPassword): m_correctUsername(std::move(correctUsername)), m_correctPassword(std::move(correctPassword)){}bool validateLogin(const std::string& username, const std::string& password) const{return username == m_correctUsername && password == m_correctPassword;}private:std::string m_correctUsername;std::string m_correctPassword;
};

ViewModel 类

ViewModel 是 MVVM 模式的核心枢纽,它有效分离了业务逻辑与视图。ViewModel 负责从 Model 获取数据,处理用户输入,并通过 ObservableField 通知视图更新。

class View; // 前向声明class ViewModel : public Observer
{
public:ViewModel(UserModel* model): m_model(model), m_view(nullptr), m_usernameField("username", ""), m_passwordField("password", ""){m_usernameField.addObserver(this);m_passwordField.addObserver(this);}void setView(View* view){m_view = view;}void setUsername(const std::string& username){m_usernameField.set(username);}void setPassword(const std::string& password){m_passwordField.set(password);}void login(){if (m_model->validateLogin(m_usernameField.get(), m_passwordField.get())){std::cout << "Login successful!" << std::endl;}else{std::cout << "Login failed! Incorrect username or password." << std::endl;}}void onChanged(const std::string& fieldName) override{if (m_view){m_view->updateField(fieldName);}}const std::string& getUsername() const{return m_usernameField.get();}const std::string& getPassword() const{return m_passwordField.get();}private:UserModel* m_model;View* m_view;ObservableField<std::string> m_usernameField;ObservableField<std::string> m_passwordField;
};

View 类

View 类负责与用户进行交互,接收用户输入并传递给 ViewModel,同时负责更新 UI 界面。

class View
{
public:explicit View(ViewModel* viewModel): m_viewModel(viewModel){}void simulateUserInput(){std::string username, password;std::cout << "Enter username: ";std::getline(std::cin, username);m_viewModel->setUsername(username);std::cout << "Enter password: ";std::getline(std::cin, password);m_viewModel->setPassword(password);m_viewModel->login();}void updateField(const std::string& fieldName){if (fieldName == "username"){std::cout << "Updated Username: " << m_viewModel->getUsername() << std::endl;}else if (fieldName == "password"){std::cout << "Updated Password: " << m_viewModel->getPassword() << std::endl;}}private:ViewModel* m_viewModel;
};

主程序 (Main)

在 main 函数中,我们创建 UserModel、ViewModel 和 View 实例,并模拟用户输入来验证登录。


int main() 
{UserModel userModel("user", "password");ViewModel viewModel(&userModel);View view(&viewModel);viewModel.setView(&view);view.simulateUserInput();
}

如果用户输入正确的用户名和密码,程序输出如下:

Enter username: user
Updated Username: user
Enter password: password
Updated Password: password
Login successful!

如果用户输入错误的用户名或密码,程序输出如下:

Enter username: a
Updated Username: a
Enter password: b
Updated Password: b
Login failed! Incorrect username or password.

总结

MVVM 架构模式作为一种先进的软件设计模式,通过引入数据双向绑定机制,成功地在 Model、View 和 ViewModel 之间构建起高效的协同工作体系。在该架构中,View 专注于展示,Model 专注于数据管理,ViewModel 则作为桥梁,负责数据转换与交互逻辑处理,DataBinding 确保了数据的自动双向流动。这种职责分离的设计极大地提高了代码的可维护性与可扩展性,降低了各组件之间的耦合度。本文通过 C++ 实现的用户登录示例,深入剖析了 MVVM 的各个组件及其实现原理,展示了 MVVM 在实际应用中的工作流程。无论是对于前端开发还是后端开发,理解和掌握 MVVM 架构模式都有助于提升软件开发的质量与效率,为构建复杂而稳定的应用程序奠定坚实的基础。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/482135.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ARM CCA机密计算安全模型之硬件强制安全

安全之安全(security)博客目录导读 [要求 R0004] Arm 强烈建议所有 CCA 实现都使用硬件强制的安全(CCA HES)。本文件其余部分假设系统启用了 CCA HES。 CCA HES 是一个可信子系统的租户——一个 CCA HES 主机(Host),见下图所示。它将以下监控安全域服务从应用处理元件(P…

【电子通识】失效分析的流程和方法

在文章:【电子通识】失效分析的基本概念-CSDN博客 中我们讲到失效分析是是指产品失效后,根据失效的现象/模式,通过分析和验证,模拟重现失效的现象,找出失效的原因,挖掘出失效的机理的活动。 同时还讲到失效模式和失效机理,并且以LED和贴片电阻做为举例。 失效模式是失效…

Flutter:页面滚动

1、单一页面&#xff0c;没有列表没分页的&#xff0c;推荐使用&#xff1a;SingleChildScrollView() return Scaffold(backgroundColor: Color(0xffF6F6F6),body: SingleChildScrollView(child: _buildView()) );2、列表没分页&#xff0c;如购物车页&#xff0c;每个item之间…

facebook欧洲户开户条件有哪些又有何优势?

在当今数字营销时代&#xff0c;Facebook广告已成为企业推广产品和服务的重要渠道。而为了更好地利用这一平台&#xff0c;广告主们需要理解不同类型的Facebook广告账户。Facebook广告账户根据其属性可分为多种类型&#xff0c;包括个人广告账户、企业管理&#xff08;BM&#…

WEB攻防-通用漏洞CSRFSSRF协议玩法内网探针漏洞利用

CSRF构造工具&#xff0c;也可以用bp构造 选中要保存的请求&#xff0c;点击Generate HTML,生成带有添加用户请求的html文件&#xff0c;然后将构造的html放在网站上&#xff0c;生成访问地址&#xff0c;诱导管理员点击链接&#xff0c;就会添加用户 start Recording之后就会…

C#面向对象之访问限制,类基础,继承

文章目录 1 访问限制1.1 简介 2 类基础讲解2.1 类定义2.2 构造函数2.2.1 构造函数2.2.2 静态构造函数2.2.3 初始化顺序2.2.4 对象初始化器 2.3 析构函数2.4 类的静态成员2.5 匿名对象2.5.1 定义2.5.2 匿名对象的创建 3 继承3.1 基类和派生类3.2 基类初始化3.3 Partial类3.3.1 定…

代码之丑第一期-缩进

各位小伙伴们&#xff0c;大家好&#xff01;咱今天就算是正式开张了。实不相瞒&#xff0c;第一期的内容早已写好&#xff0c;但唯独这开篇方式&#xff0c;笔者想了好些时间&#xff0c;包括但不限于如下风格&#xff1a; 斗破苍穹式&#xff08;已经三刷&#xff09;&#…

JVM 性能调优 -- JVM常用调优工具【jps、jstack、jmap、jstats 命令】

前言&#xff1a; 前面我们分析怎么去预估系统资源&#xff0c;怎么去设置 JVM 参数以及怎么去看 GC 日志&#xff0c;本篇我们分享一些常用的 JVM 调优工具&#xff0c;我们在进行 JVM 调优的时候&#xff0c;通常需要借助一些工具来对系统的进行相关分析&#xff0c;从而确定…

linux上离线部署Mysql5.7.22

官网下载地址: https://downloads.mysql.com/archives/community/ Mysql安装步骤&#xff1a; 1.上传mysql安装包 上传 mysql-5.7.22-linux-glibc2.12-x86_64.tar.gz 到服务器指定目录 2.解压缩 tar -zxvf mysql-5.7.22-linux-glibc2.12-x86_64.tar.gz 3.修改名称 mv mysq…

日志与线程池

这里写自定义目录标题 日志Log.hpp测试main.cpp结果 线程池线程池的种类ThreadPool.hpp测试 Task.hpp 和 main.cppTask.hppmain.cpp结果 线程池的单例模式实现方式SignalThreadPool.hpp测试main.cpp 线程安全与重入问题死锁死锁的四个必要条件 日志 日志需要包含的信息 • 时间…

1.1 数据结构的基本概念

1.1.1 基本概念和术语 一、数据、数据对象、数据元素和数据项的概念和关系 数据&#xff1a;是客观事物的符号表示&#xff0c;是所有能输入到计算机中并被计算机程序处理的符号的总称。 数据是计算机程序加工的原料。 数据对象&#xff1a;是具有相同性质的数据元素的集合&…

泷羽sec学习打卡-shell命令5

声明 学习视频来自B站UP主 泷羽sec,如涉及侵权马上删除文章 笔记的只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都 与本人无关,切莫逾越法律红线,否则后果自负 关于shell的那些事儿-shell5 字符串运算符逻辑运算符之布尔运算符实践是检验真理的唯一标准 字符串运算…

Elasticearch索引mapping写入、查看、修改

作者&#xff1a;京东物流 陈晓娟 一、ES Elasticsearch是一个流行的开源搜索引擎&#xff0c;它可以将大量数据快速存储和检索。Elasticsearch还提供了强大的实时分析和聚合查询功能&#xff0c;数据模式更加灵活。它不需要预先定义固定的数据结构&#xff0c;可以随时添加或修…

Mybatis Plus 增删改查方法(一、增)

先定义一个简单的测试表&#xff0c;执行脚本如下&#xff1a; create table user(id bigint primary key auto_increment,name varchar(255) not null,age int not null default 0 check (age > 0) ); 根据Spingbootmybatisplus的结构根据表自行构建结构&#xff0c;大致…

本地部署 WireGuard 无需公网 IP 实现异地组网

WireGuard 是一个高性能、极简且易于配置的开源虚拟组网协议。使用路由侠内网穿透使其相互通讯。 第一步&#xff0c;服务端&#xff08;假设为公司电脑&#xff09;和客户端&#xff08;假设为公司外的电脑&#xff09;安装部署 WireGuard 1&#xff0c;点此下载&#xff08;…

unity中添加预制体及其基本设置

unity中添加预制体及其基本设置 Unity 中使用预制体的好处使用示例代码解释 Unity 中使用预制体的好处 1. 提高代码复用性 预制体可将一个游戏对象及其所有组件、子对象和设置存储在一个资源文件中&#xff0c;然后在项目中多次使用这个资源。这大大提高了代码的复用性&#x…

给定一个整数可能为正,0,负数,统计这个数据的位数.

题目描述 给定一个整数可能为正,0,负数,统计这个数据的位数. 例如1234567输出7位; -12345678输出8位;0输出1位 代码实现 int main() { long long m; long long n; scanf("%lld",&n); mn; int count0;//位数 do { count; n/10;//舍弃个位 }while(n!0); printf(&…

Linux:文件系统inode

早期&#xff0c;存储文件的设备是磁盘&#xff08;当下的市场几乎都是SSD&#xff09;&#xff0c;但大家习惯的把它们都称为磁盘&#xff0c;磁盘是用来表示区分内存的存储设备。而在操作系统看来&#xff0c;这个存储设备的结构就是一个线性结构&#xff0c;这一点很重要。 …

C++STL之vector(超详细)

CSTL之vector 1.vector基本介绍2.vector重要接口2.1.构造函数2.2.迭代器2.3.空间2.3.1.resize2.3.2.capacity 2.4.增删查找 3.迭代器失效4.迭代器分类 &#x1f31f;&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f;&#x1f31f; &#x1f680;&#x1f68…

深入浅出机器学习中的梯度下降算法

大家好&#xff0c;在机器学习中&#xff0c;梯度下降算法&#xff08;Gradient Descent&#xff09;是一个重要的概念。它是一种优化算法&#xff0c;用于最小化目标函数&#xff0c;通常是损失函数。梯度下降可以帮助找到一个模型最优的参数&#xff0c;使得模型的预测更加准…