c++面试11_什么是高内聚低耦合

🧩 一、核心概念

概念 含义 理想状态
内聚(Cohesion) 模块内部功能之间的关联程度 一个模块只做一类事情(单一职责
耦合(Coupling) 模块之间的依赖程度 模块之间相互独立,接口清晰、依赖最小

简言之:

高内聚 → 模块内部紧密合作
低耦合 → 模块之间松散关联

⚙️ 二、设计原则(必须掌握)

  1. 单一职责原则 (SRP)

    每个模块或类只负责一项功能。

    • ✅ 好:Logger 只负责日志,不负责打印格式。

    • ❌ 坏:一个 Utils 里什么工具函数都有。

  2. 开闭原则 (OCP)

    对扩展开放,对修改关闭。

    → 使用接口、多态、配置等方式扩展功能,而不是改老代码。

  3. 依赖倒置原则 (DIP)

    高层模块不依赖低层模块,两者都依赖抽象。

    → 用接口代替具体实现。

  4. 接口隔离原则 (ISP)

    一个接口不应该被强迫依赖它不使用的方法。

    → 小接口更容易复用。

  5. 迪米特法则(最少知道原则)

    模块之间尽量少打交道,只与直接朋友通信。

    → 减少隐性依赖。

  6. 🔨 三、实现高内聚低耦合的常用方法

技术/方法 说明
模块化设计 将系统按功能拆分成独立模块(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++ 服务系统中,这意味着:

  • 抽象接口 + 依赖注入 + 分层架构 实现松耦合;

  • 职责单一 + 内部封装 保证高内聚。

相关推荐
han_19 小时前
从一道前端面试题,谈 JS 对象存储特点和运算符执行顺序
前端·javascript·面试
蝎子莱莱爱打怪1 天前
我的2025年年终总结
java·后端·面试
我的写法有点潮1 天前
JS中对象是怎么运算的呢
前端·javascript·面试
镜花水月linyi1 天前
Cookie、Session、JWT 的区别?
后端·面试
青莲8431 天前
Java内存模型(JMM)与JVM内存区域完整详解
android·前端·面试
青莲8431 天前
Java内存回收机制(GC)完整详解
java·前端·面试
CCPC不拿奖不改名1 天前
python基础:python语言中的函数与模块+面试习题
开发语言·python·面试·职场和发展·蓝桥杯
测试老哥1 天前
2026最新软件测试面试热点问题(含答案+文档)
自动化测试·软件测试·python·测试工具·面试·职场和发展·测试用例
青莲8431 天前
Java并发编程高级(线程池·Executor框架·并发集合)
android·前端·面试
洛小豆1 天前
她问我:服务器快被垃圾文件塞爆了,怎么破?我说:给文件办个“临时居住证”
后端·面试