当前位置: 首页 > news >正文

【设计模式】SOLID 设计原则概述

SOLID 是面向对象设计中的五大原则,不管什么面向对象的语言, 这个准则都很重要,如果你没听说过,赶紧先学一下。它可以提高代码的可维护性可扩展性可读性,使代码更加健壮、易于测试和扩展。SOLID 代表以下五个设计原则:

  1. S - 单一职责原则(Single Responsibility Principle, SRP)
  2. O - 开闭原则(Open/Closed Principle, OCP)
  3. L - 里氏替换原则(Liskov Substitution Principle, LSP)
  4. I - 接口隔离原则(Interface Segregation Principle, ISP)
  5. D - 依赖倒置原则(Dependency Inversion Principle, DIP)

1. 单一职责原则(SRP)

一个类应该仅有一个引起它变化的原因
一个类应该仅负责一个功能,否则后期修改代码时,可能会影响其他无关功能。

❌ 违反 SRP 的例子

class Report {
public:
    void generateReport() { /* 生成报表 */ }
    void printReport() { /* 打印报表 */ }
    void saveToFile() { /* 保存到文件 */ }
};

问题:
Report 负责 生成报表打印报表保存报表,违反了 SRP。

✅ 遵循 SRP 的改进

class Report {
public:
    void generateReport() { /* 生成报表 */ }
};

class ReportPrinter {
public:
    void printReport(Report& report) { /* 打印报表 */ }
};

class ReportSaver {
public:
    void saveToFile(Report& report) { /* 保存到文件 */ }
};

改进点:

  • Report 只负责生成报表
  • ReportPrinter 负责打印
  • ReportSaver 负责存储

这样每个类的变更都不会影响其他功能,符合 SRP。


2. 开闭原则(OCP)

软件实体(类、模块、函数)应该对扩展开放,对修改关闭。
即:新增功能时,应该通过扩展代码,而不是修改已有代码

❌ 违反 OCP 的例子

class PaymentProcessor {
public:
    void processPayment(std::string paymentType) {
        if (paymentType == "CreditCard") {
            // 处理信用卡支付
        } else if (paymentType == "PayPal") {
            // 处理 PayPal 支付
        }
    }
};

问题:

  • 如果新增 Apple Pay,需要修改 processPayment(),违反 OCP。
  • 代码越复杂,修改的风险越高。

✅ 遵循 OCP 的改进

class Payment {
public:
    virtual void pay() = 0;
    virtual ~Payment() = default;
};

class CreditCardPayment : public Payment {
public:
    void pay() override { /* 处理信用卡支付 */ }
};

class PayPalPayment : public Payment {
public:
    void pay() override { /* 处理 PayPal 支付 */ }
};

class PaymentProcessor {
public:
    void processPayment(Payment& payment) {
        payment.pay();
    }
};

改进点:

  • 新增支付方式时,不需要修改 PaymentProcessor,只需新增一个类(符合 OCP)。
  • 通过 多态 使代码更加灵活。

3. 里氏替换原则(LSP)

子类必须能够替换基类,并且不会破坏程序的正确性

❌ 违反 LSP 的例子

class Bird {
public:
    virtual void fly() { /* 飞行逻辑 */ }
};

class Penguin : public Bird {
public:
    void fly() override {
        throw std::runtime_error("企鹅不会飞!");
    }
};

问题:

  • Penguin 继承了 Bird,但企鹅不会飞!
  • Penguin::fly() 违背了父类的逻辑,可能导致程序崩溃。

✅ 遵循 LSP 的改进

class Bird {
public:
    virtual void move() = 0;
};

class FlyingBird : public Bird {
public:
    void move() override { /* 飞行逻辑 */ }
};

class Penguin : public Bird {
public:
    void move() override { /* 企鹅走路 */ }
};

改进点:

  • 抽象出 FlyingBirdPenguin,使 Penguin 不继承 fly(),从而避免违反 LSP。

4. 接口隔离原则(ISP)

不应该强迫类实现它们不需要的接口
即:一个接口不应该承担过多职责,而应该拆分成多个专门的接口

❌ 违反 ISP 的例子

class Worker {
public:
    virtual void work() = 0;
    virtual void eat() = 0;
};
class Robot : public Worker {
public:
    void work() override { /* 机器人工作 */ }
    void eat() override { throw std::runtime_error("机器人不吃饭!"); }
};

问题:

  • Robot 不需要 eat(),但仍然要实现它,违反 ISP

✅ 遵循 ISP 的改进

class Workable {
public:
    virtual void work() = 0;
};

class Eatable {
public:
    virtual void eat() = 0;
};

class Human : public Workable, public Eatable {
public:
    void work() override { /* 人工作 */ }
    void eat() override { /* 人吃饭 */ }
};

class Robot : public Workable {
public:
    void work() override { /* 机器人工作 */ }
};

改进点:

  • Worker 拆分成 WorkableEatable,避免不必要的实现。

5. 依赖倒置原则(DIP)

高层模块不应该依赖低层模块,而应该依赖于抽象(接口)

❌ 违反 DIP 的例子

class LEDLight {
public:
    void turnOn() { /* 打开 LED 灯 */ }
};

class Switch {
private:
    LEDLight light;
public:
    void operate() { light.turnOn(); }
};

问题:

  • Switch 直接依赖 LEDLight,如果要支持 白炽灯,必须修改 Switch,违反 OCP 和 DIP

✅ 遵循 DIP 的改进

class Light {
public:
    virtual void turnOn() = 0;
    virtual ~Light() = default;
};

class LEDLight : public Light {
public:
    void turnOn() override { /* 打开 LED 灯 */ }
};

class IncandescentLight : public Light {
public:
    void turnOn() override { /* 打开白炽灯 */ }
};

class Switch {
private:
    Light& light;
public:
    Switch(Light& l) : light(l) {}
    void operate() { light.turnOn(); }
};

改进点:

  • Switch 依赖 Light 接口,不依赖具体的 LEDLight,符合 DIP
  • 以后要支持新灯泡 不修改 Switch 代码。

总结

原则含义好处
SRP一个类只做一件事降低耦合,提高可维护性
OCP类应对扩展开放,对修改关闭增加功能时不修改已有代码
LSP子类可以替换父类,不影响功能确保继承不会破坏系统
ISP接口应该小而精,不要强迫实现不需要的功能降低实现负担,提高灵活性
DIP依赖接口,不依赖具体实现提高可扩展性,降低耦合

结论

遵循 SOLID 原则可以写出更好的代码,使系统更易扩展、维护和测试。在设计类和模块时,应尽量减少耦合,提高可复用性,避免不必要的修改。

相关文章:

  • oracle 索引
  • 【鸿蒙开发】Hi3861学习笔记- WIFI应用AP建立网络
  • Python---数据分析(Pandas六:二维数组DataFrame,DataFrame的创建,DataFrame的属性)
  • CSS实现当鼠标悬停在一个元素上时,另一个元素的样式发生变化的效果
  • 前端网络请求
  • 面向对象(进阶)(‘封装‘,‘多态‘,‘对象属性‘,‘类属性‘,‘类方法‘,‘对象方法‘及其应用场景)
  • 糊涂人寄信——递推
  • 算法设计与分析——动态规划
  • KnowGPT知识图谱整合
  • 深入浅出理解LLM PPO:基于verl框架的实现解析之一
  • Java并发编程面试题:锁(17题)
  • c++ 数组索引越界检查
  • 解决 C 盘空间不足,免费软件高效清理
  • Python 用户账户(创建用户账户)
  • GaussDB构建高性能Schema:分布式数据库架构设计与实战
  • python NameError报错之导库报错
  • C++代码2-多目标算法求解车辆路径规划
  • 阻止 Mac 在运行任务时进入休眠状态
  • Linux python 安装 conda(内部自带的有python的版本了)
  • 通俗详解redis底层数据结构哈希表之渐进式rehash
  • 上海比常年平均时间提前12天入夏,明天最高气温可达33℃
  • 中欧互动中的合作与分歧:务实需求将克服泛安全化的“政治钟摆”
  • 中国进出口银行:1-4月投放制造业中长期贷款超1800亿元
  • 一周文化讲座|“我的生命不过是温柔的疯狂”
  • 我国城市规划“全面体检”套餐出台,城市体检将逐步与供地计划等挂钩
  • 美联储主席:供应冲击或更频繁,将重新评估货币政策方法中的通胀和就业因素