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++ 服务系统中,这意味着:

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

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

相关推荐
Lee川12 小时前
优雅进化的JavaScript:从ES6+新特性看现代前端开发范式
javascript·面试
Lee川16 小时前
从异步迷雾到优雅流程:JavaScript异步编程与内存管理的现代化之旅
javascript·面试
晴殇i18 小时前
揭秘JavaScript中那些“不冒泡”的DOM事件
前端·javascript·面试
绝无仅有18 小时前
Redis过期删除与内存淘汰策略详解
后端·面试·架构
绝无仅有18 小时前
Redis大Key问题排查与解决方案全解析
后端·面试·架构
AAA梅狸猫19 小时前
Looper.loop() 循环机制
面试
AAA梅狸猫19 小时前
Handler基本概念
面试
Wect20 小时前
浏览器缓存机制
前端·面试·浏览器
掘金安东尼20 小时前
Fun with TypeScript Generics:玩转 TS 泛型
前端·javascript·面试
掘金安东尼20 小时前
Next.js 企业级落地
前端·javascript·面试