设计原则:单一职责、开闭原则、里氏替换、接口隔离和依赖倒转原则
面向对象编程中的五大设计原则: 单一职责原则、开闭原则、里氏替换、接口隔离和依赖倒转原则
1. 单一职责原则
- 一个类应该只有一个引起它变化的原因,即一个类只负责一项职责。
- 如果一个类承担了过多的职责,那么它的耦合性会变高,修改其中一个职责可能会影响其他职责。
示例
// 违反 单一职责原则 的示例
class Report {
public:
void generateReport() {
// 生成报告
}
void saveToFile() {
// 保存报告到文件
}
};
// 遵循 单一职责原则 的示例
class Report {
public:
void generateReport() {
// 生成报告
}
};
class ReportSaver {
public:
void saveToFile(const Report& report) {
// 保存报告到文件
}
};
优点
- 提高类的内聚性。
- 降低类的复杂度,便于维护和测试。
2. 开闭原则
- 软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。
- 当需求变化时,应该通过扩展来实现新功能,而不是修改现有代码。
示例
// 违反 开闭原则 的示例
class Rectangle {
public:
double width, height;
};
class AreaCalculator {
public:
double calculateArea(Rectangle& shape) {
return shape.width * shape.height;
}
};
// 如果需要支持圆形,必须修改 AreaCalculator
class Circle {
public:
double radius;
};
// 遵循 开闭原则 的示例
class Shape {
public:
virtual double calculateArea() = 0; // 抽象方法
};
class Rectangle : public Shape {
public:
double width, height;
double calculateArea() override {
return width * height;
}
};
class Circle : public Shape {
public:
double radius;
double calculateArea() override {
return 3.14 * radius * radius;
}
};
优点
- 提高代码的可扩展性。
- 减少对现有代码的修改,降低引入新错误的风险。
3. 里氏替换原则
- 子类必须能够替换其父类,并且替换后程序的行为不会发生变化。
- 子类应该完全实现父类的行为,而不是改变父类的行为。
示例
// 违反 里氏替换原则 的示例
class Bird {
public:
virtual void fly() {
// 飞行
}
};
class Penguin : public Bird {
public:
void fly() override {
throw std::runtime_error("Penguins can't fly!");
}
};
// 遵循 里氏替换原则 的示例
class Bird {
public:
virtual void move() = 0; // 抽象方法
};
class FlyingBird : public Bird {
public:
void move() override {
fly();
}
virtual void fly() = 0;
};
class Penguin : public Bird {
public:
void move() override {
swim();
}
void swim() {
// 游泳
}
};
优点
- 确保继承关系的正确性。
- 增强代码的可复用性和可维护性。
4. 接口隔离原则
- 客户端不应该依赖它不需要的接口。
- 应该将大而全的接口拆分为小而专的接口,避免实现类被迫实现不需要的方法。
示例
// 违反 接口隔离原则 的示例
class Machine {
public:
virtual void print() = 0;
virtual void scan() = 0;
virtual void fax() = 0;
};
class MultiFunctionPrinter : public Machine {
public:
void print() override {
// 打印
}
void scan() override {
// 扫描
}
void fax() override {
// 传真
}
};
class SimplePrinter : public Machine {
public:
void print() override {
// 打印
}
void scan() override {
throw std::runtime_error("Not supported!");
}
void fax() override {
throw std::runtime_error("Not supported!");
}
};
// 遵循 接口隔离原则 的示例
class Printer {
public:
virtual void print() = 0;
};
class Scanner {
public:
virtual void scan() = 0;
};
class Fax {
public:
virtual void fax() = 0;
};
class SimplePrinter : public Printer {
public:
void print() override {
// 打印
}
};
优点
- 减少接口的冗余。
- 提高代码的灵活性和可维护性。
5. 依赖倒置原则
- 高层模块不应该依赖低层模块,二者都应该依赖抽象。
- 抽象不应该依赖细节,细节应该依赖抽象。
示例
// 违反 依赖倒置原则 的示例
class LightBulb {
public:
void turnOn() {
// 开灯
}
};
class Switch {
private:
LightBulb bulb;
public:
void operate() {
bulb.turnOn();
}
};
// 遵循 依赖倒置原则 的示例
class Switchable {
public:
virtual void turnOn() = 0;
};
class LightBulb : public Switchable {
public:
void turnOn() override {
// 开灯
}
};
class Switch {
private:
Switchable& device;
public:
Switch(Switchable& device) : device(device) {}
void operate() {
device.turnOn();
}
};
优点
- 降低模块间的耦合性。
- 提高代码的可扩展性和可维护性。
总结
- 单一职责原则:一个类只负责一项职责。
- 开闭原则:对扩展开放,对修改关闭。
- 里氏替换原则:子类可以替换父类。
- 接口隔离原则:接口应该小而专。
- 依赖倒置原则:依赖抽象,而不是具体实现。
遵循 SOLID 原则可以帮助开发者设计出更健壮、灵活和可维护的软件系统。