智能指针
一、背景知识
回忆我们最初学Qt
的时候,经常通常都是在一个文件中,创建很多控件,然后在当前文件中处理该控件,包括信号槽的处理,控件的变化等等…我们最初用的就是所谓的命令式编程 也就是MVC
随着我们的项目愈发庞大,我们发现,我们的代码越来越难以维护,并且出现的bug
很难定位,俗称(屎山),后面我们了解到声明式编程 也就是MVVM
二、命令式编程 vs 声明式编程
2.1 命令式编程(Imperative Programming)
命令式编程是一种通过一系列指令和命令来改变程序状态的范式。开发者需要显式地描述如何进行某些操作,通常通过控制流(如条件语句、循环等),逐步指示计算机完成任务。这种方式强调“如何做”。
👁️👁️特点:
- 步骤驱动:开发者需要明确每个操作步骤。
- 状态管理:程序的状态在每一步变化中逐渐改变。
- 控制流:使用条件和循环来控制程序的执行顺序。
根据以下实例,我们通过信号和槽直接处理用户的点击事件,通过显式地调用方法来更新UI
。
#include <QApplication>
#include <QPushButton>
#include <QLabel>
#include <QVBoxLayout>
#include <QWidget>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QWidget window;
QVBoxLayout layout(&window);
QLabel label("Hello, World!");
QPushButton button("Change Text");
layout.addWidget(&label);
layout.addWidget(&button);
// 命令式风格
QObject::connect(&button, &QPushButton::clicked, [&]() {
label.setText("Text Changed!");
});
window.show();
return a.exec();
}
2.2 声明式编程(Declarative Programming)
声明式编程是一种通过声明或表达程序所需的结果而不是详细说明实现过程的范式。在这种方式中,开发者告诉计算机“做什么”,而不必去说明“怎么做”。这通常通过描述数据结构、约束条件或使用高级抽象来实现,降低了复杂度。
👁️👁️特点:
- 结果驱动:开发者关注于程序的目标,而不是实现细节。
- 数据抽象:数据和逻辑的分离,通常使用模型来管理数据。
- 更高的可维护性:由于代码更简洁,结构更清晰,通常更易于维护和扩展。
参考以下代码,数据的管理与视图的显示分开,模型负责处理数据,视图只关心如何展示,而不需关心背后的逻辑。
#include <QApplication>
#include <QListView>
#include <QStringListModel>
#include <QVBoxLayout>
#include <QWidget>
#include <QPushButton>
class MyModel : public QStringListModel {
public:
MyModel(QObject *parent = nullptr) : QStringListModel(parent) {
setStringList({"Item 1", "Item 2", "Item 3"});
}
void addItem(const QString &item) {
QStringList items = stringList();
items.append(item);
setStringList(items);
}
};
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QWidget window;
QVBoxLayout layout(&window);
MyModel model;
QListView view;
view.setModel(&model);
QPushButton button("Add Item");
QObject::connect(&button, &QPushButton::clicked, [&]() {
model.addItem("New Item");
});
layout.addWidget(&view);
layout.addWidget(&button);
window.show();
return a.exec();
}
在Qt中,常见的数据和视图分离方式有使用Model-View-Controller (MVC)、Model-View-ViewModel (MVVM) 等设计模式。这些方法允许更清晰的分离数据逻辑和用户界面,从而提高代码的可维护性和可扩展性。
三、 MVC(Model-View-Controller)
MVC(Model-View-Controller)是一种软件架构模式,用于实现用户界面和应用程序之间的分离。这种模式帮助组织代码,提高了应用程序的可维护性和可扩展性。MVC 将应用程序分为三个主要组件:模型(Model)、视图(View)和控制器(Controller)。
3.1 模型(Model)
模型负责处理应用程序的数据和业务逻辑。它直接与数据库交互,进行数据的存取和处理。在 MVC 模式中,模型独立于用户界面和控制逻辑,确保应用程序的数据状态保持一致。
👁️👁️功能:
- 管理应用程序的数据。
- 执行业务逻辑,包括数据验证和计算。
- 通知视图更新的数据状态(通常通过观察者模式)。
3.2 视图(View)
视图是用户界面部分,负责显示数据和呈现给用户。它从模型获取数据,并根据这些数据生成视觉输出。视图只是负责表现,不直接处理数据逻辑。
👁️👁️功能:
- 提供用户交互的界面。
- 显示模型中的数据。
- 监听用户输入,并将其转发给控制器。
3.3 控制器(Controller)
控制器是连接模型和视图的中介。它接收用户的输入,处理请求,并更新模型或视图。控制器会根据用户操作的类型选择相应的模型和视图进行处理。
👁️👁️功能:
- 接收用户输入和事件(如按钮点击)。
- 调用模型以获取或修改数据。
- 选择和更新适当的视图以呈现新的数据。
3.4 MVC 工作流程
- 用户与视图交互(例如,点击按钮)。
- 视图将用户的输入传递给控制器。
- 控制器处理输入,并可能请求模型更新(例如,保存用户修改的数据)。
- 模型更新后,会通知视图更新(通常通过观察者模式)。
- 视图接收到更新并重新渲染以显示新数据。
👁️👁️优点
- 分离关注点:每个组件的职责明确,代码更清晰。
- 可维护性高:分离使得修改任何一个部分对其他部分的影响减小。
- 可扩展性:容易添加新功能或修改现有功能;例如,可以独立于模型数据重构视图。
- 复用性:模型和视图可以在不同的应用程序中复用。
3.5 总结
MVC 是一种非常流行的设计模式,广泛用于Web应用程序和桌面应用程序开发。通过将用户界面与业务逻辑清晰分离,MVC 提供了一个灵活和高效的开发方式,有助于构建可维护和可扩展的应用程序。
MVVM(Model-View-ViewModel)是一种软件架构模式,主要用于构建用户界面,尤其在需要双向数据绑定的应用程序中非常常见。MVVM 模式通过分离关注点,提高了代码的可维护性和可测试性。它将应用程序分为三个主要组件:模型(Model)、视图(View)和视图模型(ViewModel)。
四、 MVVM(Model-View-ViewModel)
是一种软件架构模式,主要用于构建用户界面,尤其在需要双向数据绑定的应用程序中非常常见。MVVM 模式通过分离关注点,提高了代码的可维护性和可测试性。它将应用程序分为三个主要组件:模型(Model)、视图(View)和视图模型(ViewModel)。
4.1 模型(Model)
模型与 MVC 中的模型类似,负责处理应用程序的数据和业务逻辑。它可以是数据模型、数据存取层,或者业务逻辑代码。
👁️👁️功能:
- 表示应用程序的核心数据和业务逻辑。
- 直接与数据库和后端进行交互。
4.2 视图(View)
视图是用户界面部分,展示用户与应用程序交互的信息。视图通常包括按钮、文本框、图形等界面元素。
👁️👁️功能:
- 提供用户交互的界面。
- 直接绑定到视图模型,以反映数据的变化。
4.3 视图模型(ViewModel)
视图模型是连接视图和模型的中介,负责处理与视图的交互逻辑,并将数据提供给视图进行展示。视图模型使用数据绑定来更新视图,同时也提供处理用户输入和事件的逻辑。
👁️👁️功能:
- 提供视图所需的数据和命令。
- 通过属性通知视图更新(通常利用数据绑定机制)。
- 处理视图的状态和行为,与模型交互。
4.5 MVVM 工作流程
- 用户与视图交互(例如,点击按钮)。
- 视图通过数据绑定获得视图模型中的数据和命令。
- 视图模型接收到用户输入并更新模型或处理业务逻辑。
- 模型更新后,视图模型通过通知机制(如属性变化通知)更新视图。
- 视图更新以反映最新的数据。
👁️👁️优点:
- 双向数据绑定:提供更简洁的方式来连接UI和数据,自动更新。
- 可测试性高:视图模型包含了用户界面的具体逻辑,易于单元测试。
- 分离关注点:模型、视图和视图模型之间的分离使得代码结构清晰。
- 复用性:视图模型可以在不同视图间复用,不依赖于具体的UI实现。
4.6 总结
MVVM
模式特别适用于需要复杂用户交互和动态数据展示的场景,如桌面应用程序和Web应用程序,尤其是在使用现代前端框架(如 Angular、Vue、React 等)时。它通过将视图与逻辑分离,提高了代码的可维护性和可扩展性。
五、 MVC vs MVVM
在Qt
框架中,MVC
(模型-视图-控制器)和MVVM
(模型-视图-视图模型)也是常见的设计模式。虽然Qt
主要倾向于使用MVVM
,但二者之间仍然存在一些关键区别。下面详细介绍这两者在Qt中的应用和差异。
5.1 MVC(模型-视图-控制器)
👁️👁️结构:
- Model: 数据结构和业务逻辑,通常用
QAbstractItemModel
及其子类(如QStandardItemModel
)来实现。 - View: 界面元素,负责数据的展示。
Qt
提供了多种视图类(如QTableView、QListView
等)。 - Controller: 管理模型和视图之间的交互,但在
Qt
中,通常视图直接与模型交互,逻辑会嵌入在信号和槽中。
👁️👁️示例:
在一个简单的联系人管理应用中:
- Model: 使用QAbstractListModel来存储联系人信息。
- View: 使用QListView来展示联系人列表。
- Controller: 视图接收输入(例如,添加新联系人),然后更新模型,通常是通过信号和槽的机制。
5.2 MVVM (模型-视图-视图模型)在Qt中的使用
👁️👁️结构:
- Model: 同样负责数据和业务逻辑,使用QAbstractItemModel等实现。
- View: 界面部分,通常使用QML(Qt Modeling Language)或Qt Widgets。
- ViewModel: 负责将Model的数据转换为View所需的数据格式和行为。这可以通过QObject和Q_PROPERTY来实现,与数据绑定特性兼容。
👁️👁️示例:
在一个更复杂的任务管理应用中:
- Model: 使用QAbstractListModel实现任务数据。
- View: QML界面显示任务列表。
- ViewModel: 一个QObject子类,提供与UI组件绑定的属性,处理用户输入(如添加或删除任务)。
5.3 区别总结
-
数据绑定:
- MVC: 视图和模型之间的关系更加直接,通常需要手动更新视图。
- MVVM: 可以利用Qt的信号和槽机制,以及Q_PROPERTY,实现更简便的数据绑定。
-
解耦:
- MVC: 控制器和视图之间的关系较紧密,可能导致代码变得复杂。
- MVVM: 通过ViewModel实现较好的解耦,增强了代码的可测试性和可维护性。
-
适用场景:
- MVC: 更适合简单的桌面应用或较少交互的应用。
- MVVM: 适合更复杂、交互性较强的应用,尤其是使用QML的应用。
👁️👁️总结:
在Qt中,尽管MVC模式仍然适用,但MVVM因其更好的可维护性和可扩展性,尤其在使用QML时,更加广泛使用。选择哪种模式通常取决于项目的复杂性、需求以及团队的技术栈。