在 C++ 中,使用 接口(抽象类) 和 依赖注入 是避免模块之间过度耦合的常见方法。以下是一个基于 Qt 的示例,展示如何通过接口和抽象类来解耦模块,并确保模块内部的函数之间不会产生不必要的依赖。
1. 示例场景
假设我们有一个 日期时间显示模块,它涉及以下层次:
- 用户界面层(UI Layer):负责显示日期时间。
- 业务逻辑层(Business Logic Layer):负责获取和格式化日期时间。
- 数据访问层(Data Access Layer):负责从系统获取原始时间数据。
为了避免耦合,我们使用接口(抽象类)来定义模块之间的交互。
2. 实现代码
2.1 定义接口(抽象类)
首先,我们定义一个接口 IDateTimeProvider
,用于获取原始时间数据。这个接口可以被不同的实现类(如系统时间提供者、模拟时间提供者)实现。
// IDateTimeProvider.h
#ifndef IDATETIMEPROVIDER_H
#define IDATETIMEPROVIDER_H#include <QDateTime>class IDateTimeProvider {
public:virtual ~IDateTimeProvider() = default;virtual QDateTime getCurrentDateTime() const = 0; // 获取当前时间
};#endif // IDATETIMEPROVIDER_H
2.2 实现接口
接下来,我们实现一个具体的类 SystemDateTimeProvider
,它从系统获取时间数据。
// SystemDateTimeProvider.h
#ifndef SYSTEMDATETIMEPROVIDER_H
#define SYSTEMDATETIMEPROVIDER_H#include "IDateTimeProvider.h"
#include <QDateTime>class SystemDateTimeProvider : public IDateTimeProvider {
public:QDateTime getCurrentDateTime() const override {return QDateTime::currentDateTime(); // 从系统获取当前时间}
};#endif // SYSTEMDATETIMEPROVIDER_H
2.3 业务逻辑层
我们创建一个 DateTimeFormatter
类,负责格式化时间数据。它依赖于 IDateTimeProvider
接口,而不是具体的实现类。
// DateTimeFormatter.h
#ifndef DATETIMEFORMATTER_H
#define DATETIMEFORMATTER_H#include "IDateTimeProvider.h"
#include <QString>class DateTimeFormatter {
public:DateTimeFormatter(IDateTimeProvider* provider) : m_provider(provider) {}QString getFormattedDateTime() const {QDateTime dateTime = m_provider->getCurrentDateTime();return dateTime.toString("yyyy-MM-dd HH:mm:ss"); // 格式化时间}private:IDateTimeProvider* m_provider; // 依赖注入
};#endif // DATETIMEFORMATTER_H
2.4 用户界面层
我们创建一个 Qt 窗口类 DateTimeWindow
,用于显示格式化后的时间。它依赖于 DateTimeFormatter
。
// DateTimeWindow.h
#ifndef DATETIMEWINDOW_H
#define DATETIMEWINDOW_H#include <QWidget>
#include <QLabel>
#include <QVBoxLayout>
#include "DateTimeFormatter.h"class DateTimeWindow : public QWidget {Q_OBJECTpublic:DateTimeWindow(DateTimeFormatter* formatter, QWidget* parent = nullptr): QWidget(parent), m_formatter(formatter) {setupUI();}private:void setupUI() {QVBoxLayout* layout = new QVBoxLayout(this);QLabel* label = new QLabel(this);label->setText(m_formatter->getFormattedDateTime()); // 显示格式化时间layout->addWidget(label);setLayout(layout);}DateTimeFormatter* m_formatter; // 依赖注入
};#endif // DATETIMEWINDOW_H
2.5 主程序
在主程序中,我们通过依赖注入的方式将各个模块组合在一起。
// main.cpp
#include <QApplication>
#include "DateTimeWindow.h"
#include "DateTimeFormatter.h"
#include "SystemDateTimeProvider.h"int main(int argc, char *argv[]) {QApplication app(argc, argv);// 创建具体的时间提供者SystemDateTimeProvider provider;// 创建格式化器,注入时间提供者DateTimeFormatter formatter(&provider);// 创建窗口,注入格式化器DateTimeWindow window(&formatter);window.show();return app.exec();
}
3. 代码结构说明
IDateTimeProvider
:- 是一个抽象类,定义了获取时间数据的接口。
- 具体的实现类(如
SystemDateTimeProvider
)可以替换为其他实现(如模拟时间提供者)。
DateTimeFormatter
:- 依赖于
IDateTimeProvider
接口,而不是具体的实现类。 - 通过依赖注入的方式获取时间数据。
- 依赖于
DateTimeWindow
:- 依赖于
DateTimeFormatter
,负责显示格式化后的时间。 - 通过依赖注入的方式获取格式化器。
- 依赖于
4. 解耦的优势
- 可扩展性:
- 如果需要更换时间提供者(如从系统时间切换到网络时间),只需实现新的
IDateTimeProvider
类,而不需要修改DateTimeFormatter
或DateTimeWindow
。
- 如果需要更换时间提供者(如从系统时间切换到网络时间),只需实现新的
- 可测试性:
- 可以通过模拟
IDateTimeProvider
的实现类来测试DateTimeFormatter
,而不依赖真实的系统时间。
- 可以通过模拟
- 职责清晰:
- 每个模块只负责一个明确的功能,符合单一职责原则。
5. 扩展与优化
- 支持更多时间格式:
- 可以在
DateTimeFormatter
中添加更多格式化选项。
- 可以在
- 动态切换时间提供者:
- 可以在运行时动态切换
IDateTimeProvider
的实现类。
- 可以在运行时动态切换
- 使用智能指针:
- 可以使用
std::shared_ptr
或QSharedPointer
来管理依赖对象的生命周期,避免内存泄漏。
- 可以使用
6. 完整代码
以下是完整的代码实现:
IDateTimeProvider.h
#ifndef IDATETIMEPROVIDER_H
#define IDATETIMEPROVIDER_H#include <QDateTime>class IDateTimeProvider {
public:virtual ~IDateTimeProvider() = default;virtual QDateTime getCurrentDateTime() const = 0;
};#endif // IDATETIMEPROVIDER_H
SystemDateTimeProvider.h
#ifndef SYSTEMDATETIMEPROVIDER_H
#define SYSTEMDATETIMEPROVIDER_H#include "IDateTimeProvider.h"
#include <QDateTime>class SystemDateTimeProvider : public IDateTimeProvider {
public:QDateTime getCurrentDateTime() const override {return QDateTime::currentDateTime();}
};#endif // SYSTEMDATETIMEPROVIDER_H
DateTimeFormatter.h
#ifndef DATETIMEFORMATTER_H
#define DATETIMEFORMATTER_H#include "IDateTimeProvider.h"
#include <QString>class DateTimeFormatter {
public:DateTimeFormatter(IDateTimeProvider* provider) : m_provider(provider) {}QString getFormattedDateTime() const {QDateTime dateTime = m_provider->getCurrentDateTime();return dateTime.toString("yyyy-MM-dd HH:mm:ss");}private:IDateTimeProvider* m_provider;
};#endif // DATETIMEFORMATTER_H
DateTimeWindow.h
#ifndef DATETIMEWINDOW_H
#define DATETIMEWINDOW_H#include <QWidget>
#include <QLabel>
#include <QVBoxLayout>
#include "DateTimeFormatter.h"class DateTimeWindow : public QWidget {Q_OBJECTpublic:DateTimeWindow(DateTimeFormatter* formatter, QWidget* parent = nullptr): QWidget(parent), m_formatter(formatter) {setupUI();}private:void setupUI() {QVBoxLayout* layout = new QVBoxLayout(this);QLabel* label = new QLabel(this);label->setText(m_formatter->getFormattedDateTime());layout->addWidget(label);setLayout(layout);}DateTimeFormatter* m_formatter;
};#endif // DATETIMEWINDOW_H
main.cpp
#include <QApplication>
#include "DateTimeWindow.h"
#include "DateTimeFormatter.h"
#include "SystemDateTimeProvider.h"int main(int argc, char *argv[]) {QApplication app(argc, argv);SystemDateTimeProvider provider;DateTimeFormatter formatter(&provider);DateTimeWindow window(&formatter);window.show();return app.exec();
}
通过这种方式,我们可以有效地解耦模块,并确保系统的可扩展性和可维护性。如果需要进一步扩展或优化,可以根据实际需求调整代码!