????????代码耦合是指不同模块间的相互依赖程度。高耦合通常被视为不良的设计,因为它会导致代码难以理解、维护和扩展。了解代码耦合的产生原因和规避方法是软件开发中的一个重要方面,并且这也是面试中常见的话题。
目录
全局变量被多个模块共享,导致模块间的隐性依赖。
// 全局变量
int globalCounter;
class ModuleA {
public:
void increment() {
globalCounter++; // 直接修改全局变量
}
};
class ModuleB {
public:
void print() {
std::cout << "Counter: " << globalCounter << std::endl; // 直接读取全局变量
}
};
一个模块直接调用另一个模块的函数或方法。
class ModuleA {
public:
void doSomething() {
// 直接调用 ModuleB 的功能
ModuleB b;
b.performAction();
}
};
class ModuleB {
public:
void performAction() {
std::cout << "Performing Action" << std::endl;
}
};
具体实现而不是接口被用于模块间的交互。
class SpecificDatabase {
public:
void connect() {
// 连接到特定数据库的实现
}
};
class Application {
SpecificDatabase db;
public:
void start() {
db.connect(); // 直接依赖具体的数据库实现
}
};
修改一个模块需要修改其他多个模块。
class ModuleA {
public:
void action() {
// ModuleA 的行为
}
};
class ModuleB {
public:
void change() {
ModuleA a;
a.action(); // 依赖于 ModuleA 的具体行为
// 任何对 ModuleA 的更改都可能需要修改 ModuleB
}
};
过度或不当使用继承,使得子类与父类高度耦合。
class Base {
public:
virtual void operation() = 0;
};
class DerivedA : public Base {
public:
void operation() override {
// 实现细节
}
};
class DerivedB : public Base {
public:
void operation() override {
// 实现细节,可能与 DerivedA 高度耦合
}
};
没有明确的模块边界,导致代码功能混杂。
class ComplexModule {
public:
void doTaskA() {
// 任务 A 的实现
}
void doTaskB() {
// 任务 B 的实现
}
// 其他多个功能,使得类过于庞大和复杂
};
使用接口和抽象类:定义清晰的接口,使得模块之间基于抽象而不是具体实现进行交互。
class IDatabase {
public:
virtual void connect() = 0;
};
class MySQLDatabase : public IDatabase {
public:
void connect() override {
// MySQL数据库的连接实现
}
};
class Application {
IDatabase* db;
public:
Application(IDatabase* database) : db(database) {}
void start() {
db->connect();
}
};
依赖注入:通过依赖注入的方式来减少模块间的直接依赖。通过构造函数实现。
class Engine {
public:
void start() {}
};
class Car {
Engine* engine;
public:
Car(Engine* e) : engine(e) {}
void start() { engine->start(); }
};
单一职责原则:每个模块只负责一项功能,有助于降低耦合。
最少知识原则:一个对象应该尽可能少地了解其他对象(只与直接的朋友通信)。
class Document {
public:
void print() {}
};
class Printer {
public:
void printDocument(Document &doc) {
doc.print();
}
};
模块化和封装:确保模块具有清晰和严格的边界,封装内部实现。
通过private成员变量和public方法进行封装。
class BankAccount {
private:
double balance;
public:
void deposit(double amount) { balance += amount; }
void withdraw(double amount) { balance -= amount; }
};
避免全局变量:使用局部变量或者类成员变量,减少不必要的全局状态。
使用类成员变量代替全局变量。
class Application {
int setting;
public:
void setSetting(int s) { setting = s; }
int getSetting() const { return setting; }
};
利用设计模式:例如,观察者模式可以用于减少事件发生者和事件处理者之间的耦合。
?观察者模式用于减少耦合。
class Observer {
public:
virtual void update() = 0;
};
class Subject {
std::vector<Observer*> observers;
public:
void addObserver(Observer* o) {
observers.push_back(o);
}
void notifyObservers() {
for (Observer* o : observers) {
o->update();
}
}
};
class ConcreteObserver : public Observer {
public:
void update() override {
// 处理更新
}
};
????????在准备面试时,重点关注代码耦合的概念及其产生原因,比如全局变量的过度使用、模块间的直接交互、缺乏抽象、改动的连锁反应、复杂的继承结构和缺乏模块化设计。为了规避这些问题,熟练掌握使用接口和抽象类、依赖注入、遵循单一职责原则、最少知识原则、模块化和封装、避免全局变量以及利用设计模式(如观察者模式)来降低耦合。在面试中,通过具体代码示例和项目经验展示你如何识别并解决耦合问题,同时展示你对这些原则和实践的深入理解。