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

SOLID原则

SOLID原则是面向对象编程(OOP)中五个基本设计原则的缩写,由罗伯特·C·马丁(Robert C. Martin)提出。遵循这些原则能让代码更易于理解、维护和扩展。

具体解释如下:

  1. S:单一职责原则

    • 定义:一个类应该只有一个引起它变化的原因,即一个类只负责一项职责。
    • 好处:降低类的复杂度,当修改某个功能时,不影响其他功能。
  2. O:开闭原则

    • 定义:软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。
    • 好处:通过添加新代码来扩展行为,而不是修改已有代码,从而减少引入错误的风险。
  3. L:里氏替换原则

    • 定义:子类型必须能够替换掉它们的父类型。换句话说,程序中的父类对象都可以被子类对象替换,且程序行为不变。
    • 好处:增强系统的健壮性,使继承关系更合理,避免子类破坏父类的功能。
  4. I:接口隔离原则

    • 定义:客户端不应该被迫依赖它不使用的方法。应该将庞大的接口拆分成更小、更具体的接口。
    • 好处:避免接口污染,减少不必要的实现,使系统更解耦。
  5. D:依赖倒置原则

    • 定义:高层模块不应该依赖低层模块,两者都应该依赖抽象(如接口或抽象类);抽象不应该依赖细节,细节应该依赖抽象。
    • 好处:降低模块间的耦合度,提高系统的灵活性和可测试性(例如,便于进行单元测试时使用模拟对象)。

简单总结:SOLID原则指导我们写出高内聚、低耦合的代码,让软件更容易应对需求变化,也更容易被团队协作维护。

我们通过一个处理订单的业务场景,用具体代码示例来说明SOLID原则。为了更直观,这里使用Java语言,但核心思想通用。


1. 单一职责原则 (SRP)

要求:一个类只负责一件事。
反例Order类既计算订单总额,又保存到数据库,还发送邮件。

// ❌ 违反SRP:这个类做了太多事情
class Order {void calculateTotal() { /* 计算逻辑 */ }void saveToDatabase() { /* 数据库操作 */ }void sendEmail() { /* 发送邮件 */ }
}

正确做法:拆分成三个独立的类,各司其职。

// ✅ 符合SRP
class OrderCalculator { void calculateTotal() { } }
class OrderRepository  { void saveToDatabase(Order order) { } }
class EmailService     { void sendEmail(Order order) { } }

好处:修改邮件逻辑时,不会影响数据库存储;测试也更容易。


2. 开闭原则 (OCP)

要求:对扩展开放,对修改关闭。
场景:需要支持不同折扣类型(普通、VIP、会员日)。

反例:每次增加新折扣都要修改DiscountCalculator类。

// ❌ 违反OCP
class DiscountCalculator {double calculate(String type, double price) {if ("VIP".equals(type)) return price * 0.8;else if ("MEMBER".equals(type)) return price * 0.9;// 增加"会员日"折扣时,必须修改这个类return price;}
}

正确做法:使用抽象接口 + 具体实现,新折扣只需新增类。

// ✅ 符合OCP
interface Discount { double apply(double price); }class VipDiscount implements Discount { public double apply(double price) { return price * 0.8; } }
class MemberDiscount implements Discount { public double apply(double price) { return price * 0.9; } }class DiscountCalculator {double calculate(Discount discount, double price) {return discount.apply(price);}
}
// 增加会员日折扣:新建 HolidayDiscount 类,无需修改已有代码

3. 里氏替换原则 (LSP)

要求:子类可以替换父类且程序行为不变。
反例RectangleSquare的继承破坏了替换性。

// ❌ 违反LSP
class Rectangle {protected int width, height;void setWidth(int w) { width = w; }void setHeight(int h) { height = h; }
}class Square extends Rectangle {void setWidth(int w) { super.setWidth(w); super.setHeight(w); }void setHeight(int h) { super.setWidth(h); super.setHeight(h); }
}// 调用处
void resize(Rectangle r) {r.setWidth(5);r.setHeight(4);// 期望面积=20,但传入Square时面积=16,行为不一致
}

正确做法:不强行用继承表达“正方形是矩形”,而是使用独立的形状接口。

// ✅ 符合LSP
interface Shape { int getArea(); }
class Rectangle implements Shape { /* 独立宽高 */ }
class Square implements Shape { /* 独立边长 */ }

4. 接口隔离原则 (ISP)

要求:不要强迫客户端依赖它不使用的方法。
反例:一个臃肿的Worker接口,包含不相干的方法。

// ❌ 违反ISP
interface Worker {void work();void eat();void sleep();
}
class Robot implements Worker {public void work() { /* 工作 */ }public void eat() { /* 机器人不需要吃饭,却必须实现 */ }public void sleep() { /* 机器人不需要睡觉 */ }
}

正确做法:拆分成多个小接口。

// ✅ 符合ISP
interface Workable { void work(); }
interface Eatable { void eat(); }
interface Sleepable { void sleep(); }class Human implements Workable, Eatable, Sleepable { ... }
class Robot implements Workable { ... }  // 只实现需要的接口

5. 依赖倒置原则 (DIP)

要求:高层模块不依赖低层模块,两者都依赖抽象。
场景OrderService直接依赖具体的MySQL存储类。

反例:高层直接依赖低层具体实现。

// ❌ 违反DIP
class MySQLOrderRepository {void save(Order order) { /* MySQL存储 */ }
}
class OrderService {private MySQLOrderRepository repo = new MySQLOrderRepository(); // 直接依赖具体类void process(Order order) { repo.save(order); }
}
// 想改用Redis存储?必须修改OrderService代码

正确做法:依赖接口,具体实现通过构造注入。

// ✅ 符合DIP
interface OrderRepository { void save(Order order); }
class MySQLOrderRepository implements OrderRepository { ... }
class RedisOrderRepository implements OrderRepository { ... }class OrderService {private OrderRepository repo;OrderService(OrderRepository repo) { this.repo = repo; } // 依赖抽象void process(Order order) { repo.save(order); }
}
// 切换存储:只需传入不同实现,OrderService代码无需改动

总结对照表

原则 核心要点 错误典型 正确做法
SRP 一个类只改一个原因 上帝类(负责计算、存储、通知) 拆分为多个专职类
OCP 扩展不修改 满屏if-else判断类型 使用接口/抽象类 + 多态
LSP 子类替换父类行为不变 正方形继承矩形导致面积错误 用组合或更抽象的接口
ISP 接口不要臃肿 一个接口包含不相关的方法 拆分为多个角色接口
DIP 依赖抽象不依赖具体 高层直接new低层实现 依赖注入 + 面向接口编程

这些原则经常需要组合使用(比如DIP常需要ISP配合),实践中不必一步到位追求完美,但理解它们能让你写出更灵活、更易维护的代码。

http://www.jsqmd.com/news/635303/

相关文章:

  • Windows11+Docker零基础部署FunASR语音转写服务(附常见错误排查)
  • 30 分钟搞定答辩 PPT!Paperxie AI 生成器:本科生的毕业开挂神器
  • 终极指南:3步解决Buzz音频转录模型下载慢的问题
  • 漂白化学热磨机械浆市场洞察:未来几年,年复合增长率(CAGR)为2.9%
  • 用C语言解决这些经典小问题:逆序数字、念整数、高精度小数,锻炼你的编程思维
  • Office 2016批量版激活全攻略:KMS和MAK密钥详细教程(含Visio)
  • 解锁博士论文“超能力”:好写作AI,学术征途的“超级外挂”
  • C#联合OpenCVSharp开发的视觉源码程序:包含模板匹配、找线找圆、预处理等功能及图像显...
  • 20251918 2025-2026-2 《网络攻防实践》第5次作业
  • 本科生论文通关 “黑科技”:Paperxie 毕业论文功能,一键搞定初稿 + 格式 + 降重
  • MATLAB图像导出终极指南:使用export_fig生成高质量学术图表 [特殊字符]
  • 嵌入式显示技术决策:Adafruit_SH1106在资源受限环境下的架构优势与性能验证
  • 玄机靶场通关笔记 _ 权限维持-Windows权限维持
  • 响应与预览数据不一样?有趣问题记录
  • 别再吹牛了,% Vibe Coding 存在无法自洽的逻辑漏洞!萍
  • Cursor Pro免费升级指南:三步解锁无限AI编程助手功能
  • 深度掌握DLSS Swapper:游戏超采样技术版本管理的工程化实践指南
  • Uformer深度解析:基于Transformer架构的高效图像复原技术实现
  • 华为无线AP5030阉割内存版切换FAT模式
  • 3分钟快速上手:用Deskreen免费将手机平板变成电脑第二屏幕
  • 2026香港本科留学申请中介哪家中介最靠谱?香港本科留学申请中介推荐 - 品牌2026
  • QGIS插件实战:集成高德API实现多模式路径规划与GIS数据融合
  • 轻流无代码设备管理:让管理变得如此简单
  • AI时代工程师的Superpowers进化论
  • 3步打造你的专属漫画图书馆:Venera跨平台漫画阅读器完全指南
  • 告别毕业论文 “渡劫” 模式:Paperxie 智能写作,一键开启高效通关路
  • 如何永久保存微信聊天记录:WeChatMsg完整指南与年度报告生成教程
  • 2026年国内安全滑触线厂家排名前十权威发布:鑫铂特电气有限公司位居榜首 - 安互工业信息
  • 多目标优化正在 silently kill your AIAgent——2024 Q2头部AI平台压测数据揭示:未做约束感知MOO的Agent任务完成率暴跌67%
  • 收藏!每个程序员都该了解的“个人AI助手”如何改变团队协作