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

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

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

相关推荐
m0_736927042 小时前
2025高频Java后端场景题汇总(全年汇总版)
java·开发语言·经验分享·后端·面试·职场和发展·跳槽
T___T2 小时前
Ajax 数据请求详解与实战
javascript·面试
AAA阿giao5 小时前
深入理解 JavaScript 的 Array.prototype.map() 方法及其经典陷阱:从原理到面试实战
前端·javascript·面试
齐生17 小时前
iOS 社招 - Runtime 相关知识点
面试
Croa-vo7 小时前
Citadel SDE 面试复盘:直面硬核算法与思维挑战的双重压力
算法·面试·职场和发展
不说别的就是很菜8 小时前
【前端面试】Vue篇
前端·vue.js·面试
在等晚安么9 小时前
力扣面试150题打卡
算法·leetcode·面试
月弦笙音9 小时前
【vue3】这些不常用的API,却很实用
前端·vue.js·面试
创码小奇客10 小时前
Spring Boot依赖排坑指南:冲突、循环依赖全解析+实操方案
后端·面试·架构