通过接口或抽象类进一步解耦

发布于:2025-03-11 ⋅ 阅读:(18) ⋅ 点赞:(0)

在 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_OBJECT

public:
    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 类,而不需要修改 DateTimeFormatterDateTimeWindow
  • 可测试性
    • 可以通过模拟 IDateTimeProvider 的实现类来测试 DateTimeFormatter,而不依赖真实的系统时间。
  • 职责清晰
    • 每个模块只负责一个明确的功能,符合单一职责原则。

5. 扩展与优化

  • 支持更多时间格式
    • 可以在 DateTimeFormatter 中添加更多格式化选项。
  • 动态切换时间提供者
    • 可以在运行时动态切换 IDateTimeProvider 的实现类。
  • 使用智能指针
    • 可以使用 std::shared_ptrQSharedPointer 来管理依赖对象的生命周期,避免内存泄漏。

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_OBJECT

public:
    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();
}

通过这种方式,我们可以有效地解耦模块,并确保系统的可扩展性和可维护性。如果需要进一步扩展或优化,可以根据实际需求调整代码!