引言
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 架构模式都有助于提升软件开发的质量与效率,为构建复杂而稳定的应用程序奠定坚实的基础。