界面架构 - MVVM (Qt)

MVVM

MVVM(Model-View-ViewModel)架构是一种旨在进一步分离界面和业务逻辑的设计模式,特别适合支持数据绑定的 UI 框架,如 Qt Quick(QML)。在 MVVM 架构中,主要包含以下三个部分:

  • Model(模型)

    与其他架构类似,Model 层负责数据存储和业务逻辑处理。在本例中,Model 的逻辑非常简单,就是计算字符串的长度。通常我们可以把这部分逻辑直接集成到 ViewModel 中,或者在更复杂的项目中将其独立出来。

  • View(视图)

    View 层负责界面展示和用户交互,通常只关心如何展示数据。通过数据绑定,View 可以自动响应 ViewModel 中数据的变化,而不需要手动更新界面。在 Qt Quick 中,View 通常由 QML 文件构成。

  • ViewModel(视图模型)

    ViewModel 层充当了 Model 与 View 之间的中介,它暴露 View 所需的数据和操作(通常以属性和方法的形式),并负责处理用户的交互逻辑。通过 Q_PROPERTY 与信号机制,ViewModel 可以将 Model 的数据转换为 View 可直接绑定的形式,从而实现自动同步更新。

MVVM 的主要特点

  • 数据绑定

    ViewModel 的属性与 View 通过数据绑定建立联系,当 ViewModel 中的数据发生变化时,View 会自动更新显示,减少了手动更新 UI 的代码。

  • 彻底解耦

    ViewModel 不依赖于 View 的具体实现,降低了耦合度,使得业务逻辑更容易单独测试和维护。

  • 增强的可测试性

    由于 ViewModel 是纯 C++ 类(或 QML 中的 JavaScript 对象),它可以独立于 UI 环境进行单元测试。

示例

示例功能

构建一个简单的应用,界面包含:

  • QLineEdit:供用户输入字符串;
  • QPushButton:当用户点击按钮时,计算 QLineEdit 中文本的字符数;
  • QLabel:显示计算后的字符数。

我们将使用 Qt Quick(QML)构建 View,并使用 C++ 实现 ViewModel 类,将业务逻辑(计算字符串长度)封装在 ViewModel 中。

示例代码

ViewModel 类(C++)

定义一个 ViewModel 类,包含两个属性:

  • inputText:用户输入的文本,支持读写;

  • result:计算结果,只读属性。当用户点击按钮时,调用 ViewModel 的槽函数计算输入文本长度,并更新 result 属性。

cpp 复制代码
// ViewModel.h
#ifndef VIEWMODEL_H
#define VIEWMODEL_H

#include <QObject>
#include <QString>

class ViewModel : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString inputText READ inputText WRITE setInputText NOTIFY inputTextChanged)
    Q_PROPERTY(QString result READ result NOTIFY resultChanged)

public:
    explicit ViewModel(QObject *parent = nullptr) : QObject(parent) {}

    QString inputText() const { return m_inputText; }
    void setInputText(const QString &text) {
        if (m_inputText != text) {
            m_inputText = text;
            emit inputTextChanged();
        }
    }

    QString result() const { return m_result; }

public slots:
    // 当按钮点击时调用此槽,计算输入文本长度
    void calculateLength() {
        int len = m_inputText.length();
        m_result = QString("长度:%1").arg(len);
        emit resultChanged();
    }

signals:
    void inputTextChanged();
    void resultChanged();

private:
    QString m_inputText;
    QString m_result;
};

#endif // VIEWMODEL_H

主函数入口(main.cpp)

在 main 函数中,我们创建 QGuiApplication 和 QQmlApplicationEngine,并将 ViewModel 实例通过上下文属性传递给 QML 层,供界面绑定和调用。

cpp 复制代码
// main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "ViewModel.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;

    // 创建 ViewModel 实例
    ViewModel viewModel;
    // 将 ViewModel 注册到 QML 上下文中,名字为 "viewModel"
    engine.rootContext()->setContextProperty("viewModel", &viewModel);

    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}

QML 文件(main.qml)

qml 复制代码
// main.qml
import QtQuick 2.12
import QtQuick.Controls 2.12

ApplicationWindow {
    visible: true
    width: 300
    height: 200
    title: "MVVM 示例"

    Column {
        spacing: 10
        anchors.centerIn: parent

        TextField {
            id: inputField
            placeholderText: "请输入文本"
            text: viewModel.inputText
            onTextChanged: viewModel.inputText = text
        }

        Button {
            text: "计算长度"
            onClicked: viewModel.calculateLength()
        }

        Text {
            text: viewModel.result
            font.pointSize: 16
        }
    }
}

在 QML 中,我们构建一个简单的界面,包含一个 TextField、一个 Button 和一个 Text。

TextField 的文本与 viewModel.inputText 绑定;

Button 的点击事件调用 viewModel.calculateLength();

Text 的显示内容绑定到 viewModel.result。

总结

在上述 MVVM 示例中:

  • ViewModel 封装了业务逻辑(计算字符串长度),并通过 Q_PROPERTY 暴露输入文本(inputText)和计算结果(result)。当用户操作界面时,ViewModel 中的数据会更新,并通过信号通知 QML 自动刷新界面。

  • View(QML 部分) 仅负责界面展示和用户交互,通过数据绑定与 ViewModel 建立联系。这样 View 完全不关心具体的业务逻辑,降低了耦合度。

  • Model 部分在这个简单示例中直接内嵌在 ViewModel 中,但在更复杂的项目中,可以将业务逻辑和数据存储进一步独立出来。

这种架构充分利用了 QML 的数据绑定特性,使得界面和业务逻辑解耦,提升了代码的可维护性和可测试性。

相关推荐
枫叶丹433 分钟前
【Qt开发】输入类控件(四)-> QSpinBox
开发语言·qt
失散132 小时前
分布式专题——26 BIO、NIO编程与直接内存、零拷贝深入辨析
java·分布式·rpc·架构·nio·零拷贝
IT小番茄4 小时前
Kubernetes云平台管理实战:故障自愈实战(四)
架构
可触的未来,发芽的智生5 小时前
新奇特:负权重橡皮擦,让神经网络学会主动遗忘
人工智能·python·神经网络·算法·架构
数据智能老司机5 小时前
建构 AI Agent 应用——保护代理式系统
架构·llm·agent
kebeiovo13 小时前
muduo网络库事件驱动模型的实现与架构
网络·架构
brzhang17 小时前
AI Agent 干不好活,不是它笨,告诉你一个残忍的现实,是你给他的工具太难用了
前端·后端·架构
brzhang17 小时前
一文说明白为什么现在 AI Agent 都把重点放在上下文工程(context engineering)上?
前端·后端·架构
罗亚方舟18 小时前
微服务故障排查
微服务·云原生·架构
深度学习实战训练营19 小时前
MnasNet:NAS 自动架构搜索
架构