🧩 一、核心概念
| 概念 | 含义 | 理想状态 |
|---|---|---|
| 内聚(Cohesion) | 模块内部功能之间的关联程度 | 一个模块只做一类事情(单一职责) |
| 耦合(Coupling) | 模块之间的依赖程度 | 模块之间相互独立,接口清晰、依赖最小 |
简言之:
高内聚 → 模块内部紧密合作
低耦合 → 模块之间松散关联
⚙️ 二、设计原则(必须掌握)
-
单一职责原则 (SRP)
每个模块或类只负责一项功能。
-
✅ 好:
Logger只负责日志,不负责打印格式。 -
❌ 坏:一个
Utils里什么工具函数都有。
-
-
开闭原则 (OCP)
对扩展开放,对修改关闭。
→ 使用接口、多态、配置等方式扩展功能,而不是改老代码。
-
依赖倒置原则 (DIP)
高层模块不依赖低层模块,两者都依赖抽象。
→ 用接口代替具体实现。
-
接口隔离原则 (ISP)
一个接口不应该被强迫依赖它不使用的方法。
→ 小接口更容易复用。
-
迪米特法则(最少知道原则)
模块之间尽量少打交道,只与直接朋友通信。
→ 减少隐性依赖。
-
🔨 三、实现高内聚低耦合的常用方法
| 技术/方法 | 说明 |
|---|---|
| 模块化设计 | 将系统按功能拆分成独立模块(UI、逻辑、数据层) |
| 分层架构 | 典型如 MVC / MVP / MVVM,让各层职责单一 |
| 接口 + 抽象类 | 使用抽象接口作为模块边界,隐藏实现细节 |
| 依赖注入(DI) | 把依赖关系交给外部容器或构造函数注入 |
| 事件/回调机制 | 模块之间通过事件或信号槽通信,而不是直接调用 |
| 设计模式应用 | 观察者、策略、工厂、适配器等都可降低耦合 |
🧠 四、示例(以 C++ 为例)
❌ 坏例(高耦合、低内聚)
class OrderProcessor {
public:
void process() {
// 订单逻辑
// 直接调用数据库操作
DBConnection db;
db.connect();
db.saveOrder(this);
// 直接打印日志
std::cout << "Order processed!" << std::endl;
}
};
好例(高内聚、低耦合)
class ILogger {
public:
virtual void log(const std::string& msg) = 0;
virtual ~ILogger() = default;
};
class IOrderRepository {
public:
virtual void save(const Order& order) = 0;
virtual ~IOrderRepository() = default;
};
class OrderProcessor {
IOrderRepository& repo;
ILogger& logger;
public:
OrderProcessor(IOrderRepository& r, ILogger& l) : repo(r), logger(l) {}
void process(const Order& order) {
repo.save(order);
logger.log("Order processed");
}
};
特点:
-
高内聚 :
OrderProcessor只负责订单处理。 -
低耦合:通过接口依赖,数据库或日志实现可自由替换(例如文件日志、远程日志)。
-
符合依赖倒置:依赖抽象而非具体实现。
🧭 五、总结口诀
模块内:功能要聚焦(高内聚)
模块间:接口要解耦(低耦合)
或者更口语化一点:
"各司其职、相互独立、通过契约沟通、不要硬绑在一起。"
🎯 目标场景
我们要设计一个典型的 Qt 上位机程序,它包含以下模块:
-
UI层:显示测试状态、用户操作按钮
-
业务逻辑层:控制测试流程(开始、停止、结果分析)
-
通信层:负责与硬件(比如测试机)通信
-
日志层:负责系统日志记录
-
🧩 一、结构设计(模块划分)
App
├── main.cpp
├── ui/
│ └── MainWindow.h / .cpp
├── core/
│ ├── TestController.h / .cpp ← 业务逻辑层(高内聚)
│ └── ICommInterface.h ← 通信接口抽象(低耦合)
├── comm/
│ ├── SerialComm.h / .cpp ← 串口实现
│ └── PcieComm.h / .cpp ← PCIe 实现
├── log/
│ └── Logger.h / .cpp
⚙️ 二、接口与实现(低耦合)
通信层接口抽象(ICommInterface.h)
#pragma once
#include <QObject>
#include <QByteArray>
class ICommInterface : public QObject {
Q_OBJECT
public:
explicit ICommInterface(QObject* parent = nullptr) : QObject(parent) {}
virtual ~ICommInterface() = default;
virtual bool connectDevice() = 0;
virtual void disconnectDevice() = 0;
virtual bool sendCommand(const QByteArray& data) = 0;
signals:
void dataReceived(const QByteArray& data);
};
串口通信实现(SerialComm.h/.cpp)
#pragma once
#include "ICommInterface.h"
#include <QSerialPort>
class SerialComm : public ICommInterface {
Q_OBJECT
QSerialPort port;
public:
bool connectDevice() override {
port.setPortName("COM3");
return port.open(QIODevice::ReadWrite);
}
void disconnectDevice() override {
port.close();
}
bool sendCommand(const QByteArray& data) override {
return port.write(data) == data.size();
}
private slots:
void onReadyRead() {
emit dataReceived(port.readAll());
}
};
✅ 通过接口实现隔离。
程序上层只依赖 ICommInterface,完全不关心底层是串口、USB 还是 PCIe
🔧 三、业务逻辑层(高内聚)
#pragma once
#include <QObject>
#include "ICommInterface.h"
#include "Logger.h"
class TestController : public QObject {
Q_OBJECT
ICommInterface* comm;
Logger* logger;
public:
TestController(ICommInterface* c, Logger* l, QObject* parent = nullptr)
: QObject(parent), comm(c), logger(l)
{
connect(comm, &ICommInterface::dataReceived, this, &TestController::onDataReceived);
}
public slots:
void startTest() {
logger->log("Test started");
comm->sendCommand("START");
}
void stopTest() {
logger->log("Test stopped");
comm->sendCommand("STOP");
}
private slots:
void onDataReceived(const QByteArray& data) {
logger->log("Data received: " + QString(data));
}
};
};
✅ TestController 只负责"测试业务逻辑",
不关心通信是怎么实现的,也不直接操作UI。
四、UI层(信号槽解耦)
#pragma once
#include <QMainWindow>
#include <QPushButton>
#include "TestController.h"
class MainWindow : public QMainWindow {
Q_OBJECT
QPushButton *startBtn, *stopBtn;
TestController* controller;
public:
MainWindow(TestController* ctrl, QWidget* parent = nullptr)
: QMainWindow(parent), controller(ctrl)
{
startBtn = new QPushButton("Start", this);
stopBtn = new QPushButton("Stop", this);
startBtn->move(50, 50);
stopBtn->move(150, 50);
connect(startBtn, &QPushButton::clicked, controller, &TestController::startTest);
connect(stopBtn, &QPushButton::clicked, controller, &TestController::stopTest);
}
};
✅ UI 只负责交互,通过信号槽调用业务层接口,
无需了解通信细节或日志实现。
🧠 五、日志模块(可替换)
#pragma once
#include <QString>
#include <QDebug>
class Logger {
public:
void log(const QString& msg) {
qDebug() << "[LOG]" << msg;
}
};
未来你可以轻松替换成:
-
文件日志(写入文件)
-
网络日志(发送到远程服务器)
-
GUI 日志面板(QTextEdit输出)
不需要修改业务层的任何代码 → 低耦合的体现。
🧭 六、main.cpp(装配层,依赖注入)
#include <QApplication>
#include "MainWindow.h"
#include "SerialComm.h"
#include "Logger.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
SerialComm comm; // 可换成 PcieComm 等其他实现
Logger logger;
TestController controller(&comm, &logger);
MainWindow w(&controller);
w.show();
return app.exec();
}
🧩 七、总结:Qt 中高内聚、低耦合的实现策略
| 层级 | 方法 | 效果 |
|---|---|---|
| 模块划分 | UI、逻辑、通信、日志独立 | 职责单一(高内聚) |
| 接口抽象 | ICommInterface | 解耦依赖(低耦合) |
| 信号槽机制 | UI ↔ Controller ↔ Comm | 无直接依赖(低耦合) |
| 依赖注入 | main.cpp 组合对象 | 易于扩展、可测试 |
| 模块隔离 | Logger、Comm 独立可替换 | 提高可维护性 |
🧱 十、简短总结口诀
高内聚 :每个模块只管自己的事;
低耦合:模块之间靠接口对话。
在 C++ 服务系统中,这意味着:
-
用 抽象接口 + 依赖注入 + 分层架构 实现松耦合;
-
用 职责单一 + 内部封装 保证高内聚。