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

SOLID软件设计原则 解析

前言:在平时coding过程中,大部分程序员可能把更多精力和时间花在功能的实现和完成上面,对于代码的可读性、可读性及可扩展性没有过多的关注,这可能会造成后期功能扩展困难、新人无法理解等问题。这里介绍一些软件代码设计原则,帮助大家提升代码质量。

目录

一、SOLID软件设计原则

二、单一职责原则(Single Responsible Principle)

二、开闭原则(Open Close Principle)

三、里氏替换原则(Liskov Substitution Principle)

四、接口隔离原则(Interface Segregation Principle)

五、依赖倒置原则(Dependency Inversion Principle)


一、SOLID软件设计原则

SOLID是面向对象软件设计中5个基础设计原则的简写,由Robert C. Martin提出,是设计模式的指导思想。这些原则包括单一职责原则、开闭原则、里氏替换原则、接口隔离原则、依赖倒置原则。下面章节分别展开介绍。

二、单一职责原则(Single Responsible Principle)

1)定义:一个类应当只做一件事情,只有一个引起它变化的原因

2)思想:避免一个类同时负责多个功能,否则对一个功能的修改可能会影响到其他功能,修改扩展成本增加。

3)示例

反例:一个UserManager类同时负责用户信息管理用户日志记录,如果日志的存储方式修改(比如从本地文件改成数据库),就需要修改这个类,违反了单一职责。

正例:拆分为两个类:

// 只负责用户信息的管理 class UserManager { public: void addUser(const std::string& username) { // 处理用户添加的逻辑 } void deleteUser(const std::string& username) { // 处理用户删除的逻辑 } }; // 只负责用户相关的日志记录 class UserLogger { public: void logUserOperation(const std::string& username, const std::string& operation) { // 处理日志记录的逻辑 } };

二、开闭原则(Open Close Principle)

1)定义:软件模块(类、函数等)应该对扩展开放,对修改关闭。

2)思想:当需要新增功能时,应当通过扩展已有代码模块来实现,而不是在已有代码上直接修改、打补丁,防止补丁上打补丁,时间长了就难以维护了。

3)示例

反例:一个ShapeCalculator类,计算不同图形的面积,如果新增一种图形(比如椭圆),就需要修改类的代码:

// 反例:新增图形需要修改这个类 class ShapeCalculator { public: double calculateArea(const std::string& shapeType, double param1, double param2) { if (shapeType == "circle") { return M_PI * param1 * param1; } else if (shapeType == "rectangle") { return param1 * param2; } // 新增椭圆需要在这里加else if return 0; } };

正例:通过抽象类+继承实现

// 抽象的图形类 class Shape { public: virtual double calculateArea() const = 0; virtual ~Shape() = default; }; // 圆形类,继承Shape class Circle : public Shape { private: double radius; public: Circle(double r) : radius(r) {} double calculateArea() const override { return M_PI * radius * radius; } }; // 矩形类,继承Shape class Rectangle : public Shape { private: double width; double height; }; // 面积计算器,只依赖抽象类,新增图形不需要修改这个类 class ShapeCalculator { public: double calculateArea(const Shape& shape) { return shape.calculateArea(); } };

上述代码中出现了虚函数和抽象类的概念,如需要可参考之前的文章:C++ 虚函数 解析指南-CSDN博客

三、里氏替换原则(Liskov Substitution Principle)

1)定义:子类对象可以替换父类对象在程序中的所有使用场景,且不会改变程序的正确性。

2)思想:子类必须完全实现父类功能,不能破坏父类的行为,避免继承关系的滥用。

3)示例

反例:正方形继承矩形,但是正方形的宽和高必须相等,修改宽的时候高也会变化,破坏了矩形的行为

class Rectangle { protected: double width; double height; public: void setWidth(double w) { width = w; } void setHeight(double h) { height = h; } double getArea() { return width * height; } }; // 反例:正方形继承矩形,但是setWidth和setHeight的行为不符合父类的约定 class Square : public Rectangle { public: void setWidth(double w) override { width = w; height = w; } void setHeight(double h) override { width = h; height = h; } };

正例:重新设计继承关系,使得正方形与矩形都继承抽象的Shape类。

四、接口隔离原则(Interface Segregation Principle)

1)定义:客户端不应当依赖它不需要的接口,一个类对另一个类的依赖应当建立在最小接口上。

2)思想:避免设计大而全的接口,接口应当拆分多个小的,高内聚、低耦合。

3)示例

反例:一个大的Worker接口,包含了所有工作相关的方法,但是不同的 worker 只需要其中一部分

// 反例:大而全的接口 class Worker { public: virtual void work() = 0; virtual void eat() = 0; virtual void sleep() = 0; }; // 机器人只需要work,但是必须实现eat和sleep class Robot : public Worker { public: void work() override { /* 工作逻辑 */ } void eat() override { /* 机器人不需要吃饭,空实现 */ } void sleep() override { /* 机器人不需要睡觉,空实现 */ } };

正例:拆分为小接口

// 只包含工作的接口 class Workable { public: virtual void work() = 0; virtual ~Workable() = default; }; // 只包含休息相关的接口 class Restable { public: virtual void eat() = 0; virtual void sleep() = 0; virtual ~Restable() = default; }; // 机器人只实现Workable接口 class Robot : public Workable { public: void work() override { /* 工作逻辑 */ } }; // 人类实现Workable和Restable接口 class HumanWorker : public Workable, public Restable { public: void work() override { /* 工作逻辑 */ } void eat() override { /* 吃饭逻辑 */ } void sleep() override { /* 睡觉逻辑 */ } };

五、依赖倒置原则(Dependency Inversion Principle)

1)定义:上层模块不应当依赖底层模块抽象不应当依赖细节,细节应当依赖抽象。

2)思想:通过抽象(抽象类或接口)实现上层与底层解耦,使得模块之间依赖关系倒置,提高灵活性。

3)示例

反例:上层模块ReportGenerator直接依赖底层模块MySQLDatabase,如果需要切换数据库(比如改成PostgreSQL),就需要修改ReportGenerator

// 反例:上层依赖底层的具体实现 class MySQLDatabase { public: std::string getData() { return "从MySQL获取的数据"; } }; class ReportGenerator { private: MySQLDatabase db; public: std::string generateReport() { return "报告内容:" + db.getData(); } };

正例:通过抽象接口解耦

// 抽象的数据库接口 class Database { public: virtual std::string getData() const = 0; virtual ~Database() = default; }; // MySQL的具体实现,依赖抽象接口 class MySQLDatabase : public Database { public: std::string getData() const override { return "从MySQL获取的数据"; } }; // PostgreSQL的具体实现,依赖抽象接口 class PostgreSQLDatabase : public Database { public: std::string getData() const override { return "从PostgreSQL获取的数据"; } }; // 高层模块依赖抽象接口,而不是具体实现 class ReportGenerator { private: const Database& db; public: // 通过构造函数注入依赖 ReportGenerator(const Database& database) : db(database) {} std::string generateReport() { return "报告内容:" + db.getData(); } };

结束语:一般这5种原则不会孤立存在,通常两三个会结合在一起使用。读者可参考进行理解消化,最终目的是应用于实际项目中,实现代码质量显著提升。

http://www.cnnetsun.cn/news/117654.html

相关文章:

  • MaxScript 实现多边形层级切换按钮
  • NideShop电商系统:打造高效在线商城的终极Node.js解决方案
  • Selenium 自动化 | 案例实战篇
  • 开源RAW图像处理工具darktable:5大核心模块构建专业摄影工作流
  • Wan2.1-I2V-14B-480P:如何在消费级GPU上实现实时图像到视频生成
  • 百度贴吧终极体验优化:baidu-tieba-userscript完整使用指南
  • HFT-Orderbook:突破传统的高性能C语言订单簿引擎
  • Stable-Dreamfusion实战指南:5步掌握文本到3D模型生成核心技术
  • 浅析NCE0130KA在功率开关设计中的应用特性
  • 学习Java27天
  • ThingsBoard物联网平台消息队列实战:3大核心技术架构深度解析
  • Free Sidecar终极指南:5分钟解锁macOS多屏扩展功能
  • Universe性能优化终极指南:cProfile与火焰图实战分析
  • DeeplxFile:免费跨平台文件翻译工具的完整使用指南
  • Qwen3-4B-FP8模型实战手册:从零开始构建智能对话应用
  • IPCA改进主成分分析法 主元分析在处理数据过程中会平等的对待每一维特征,即认为每一维特征的权...
  • Carsim+Simulink联合仿真实现换道超车及弯道道路处理演示
  • 测试代码如何成为团队通用语言:从技术债到沟通桥梁的蜕变之路
  • 低代码、RPA融合、云边协同……盘点五大AI Agent平台为开发者带来的机遇与挑战。
  • 智能体(Agent)全景解析:技术路线、落地实践与产业生态
  • 3步搞定:这款智能LLM微调工具让数据准备如此简单
  • 百度网盘下载加速神器:免费解析工具完整使用指南
  • OpenUSD工具链深度解析:从入门到精通的完整指南
  • 多任务调度终极指南:从并发控制到性能优化的完整解析
  • 高效服务器监控:5步快速定位性能问题的终极指南
  • 基于SpringBoot+Vue的石材厂售卖系统(支付宝沙盒支付、协同过滤算法、物流快递API、websocket实时聊天、Echarts图形化分析)
  • ComfyUI-Manager安全级别配置深度解析与实战指南
  • COLMAP三维重建技术:从多视图图像到精准三维模型的完整指南
  • 基于Android的音乐播放器应用设计与实现6(论文+源码)
  • 如何快速掌握Unity终极REST客户端:异步网络通信完整指南