SOLID 五大原则
SOLID 是面向对象设计中最著名的五个设计原则的首字母缩写,由 Robert C. Martin(Uncle Bob)提出。遵循这些原则可以创建更易维护、更灵活、更易扩展的代码。
1. S - 单一职责原则(Single Responsibility Principle, SRP)
核心思想
一个类应该只有一个引起它变化的原因
换句话说:一个类只负责一项职责
❌ 违反 SRP 的例子
public class Order {
public void createOrder(OrderData data) {
// 创建订单逻辑
}
public void calculatePrice(Order order) {
// 价格计算逻辑
}
public void sendEmail(Order order) {
// 发送邮件逻辑
}
public void saveToDatabase(Order order) {
// 数据库操作逻辑
}
}
问题: 这个类有 4 个职责,任何一个职责变化都会导致类被修改
✅ 遵循 SRP 的例子
// 订单实体
public class Order {
private String id;
private double price;
// ...
}
// 订单服务 - 负责业务逻辑
public class OrderService {
public void createOrder(OrderData data) { }
public void calculatePrice(Order order) { }
}
// 邮件服务 - 负责通知
public class EmailService {
public void sendOrderConfirmation(Order order) { }
}
// 数据访问 - 负责持久化
public class OrderRepository {
public void save(Order order) { }
}
2. O - 开闭原则(Open-Closed Principle, OCP)
核心思想
对扩展开放,对修改关闭
软件实体(类、模块、函数等)应该在不修改现有代码的情况下进行扩展
❌ 违反 OCP 的例子
public class PaymentProcessor {
public void processPayment(String paymentType, double amount) {
if ("alipay".equals(paymentType)) {
// 支付宝支付逻辑
} else if ("wechat".equals(paymentType)) {
// 微信支付逻辑
} else if ("bank".equals(paymentType)) {
// 银行支付逻辑
}
// 每新增一种支付方式,都要修改这个方法!
}
}
✅ 遵循 OCP 的例子
// 抽象接口
public interface PaymentStrategy {
void pay(double amount);
}
// 具体实现
public class AlipayStrategy implements PaymentStrategy {
public void pay(double amount) {
// 支付宝支付逻辑
}
}
public class WechatPayStrategy implements PaymentStrategy {
public void pay(double amount) {
// 微信支付逻辑
}
}
// 支付处理器 - 不需要修改
public class PaymentProcessor {
public void process(PaymentStrategy strategy, double amount) {
strategy.pay(amount);
}
}
// 使用时扩展即可,无需修改现有代码
PaymentProcessor processor = new PaymentProcessor();
processor.process(new AlipayStrategy(), 100.0);
3. L - 里氏替换原则(Liskov Substitution Principle, LSP)
核心思想
子类必须能够替换它们的基类而不影响程序的正确性
子类应该扩展父类的功能,而不是改变父类的功能
❌ 违反 LSP 的例子
public class Rectangle {
protected int width;
protected int height;
public void setWidth(int width) {
this.width = width;
}
public void setHeight(int height) {
this.height = height;
}
public int getArea() {
return width * height;
}
}
// 正方形继承长方形 - 违反了 LSP
public class Square extends Rectangle {
@Override
public void setWidth(int width) {
this.width = width;
this.height = width; // 强制宽高相等
}
@Override
public void setHeight(int height) {
this.width = height;
this.height = height; // 强制宽高相等
}
}
// 使用场景 - 用正方形替换长方形会出问题
public void testRectangle(Rectangle rect) {
rect.setWidth(5);
rect.setHeight(10);
System.out.println(rect.getArea());
// 期望:50
// 如果传入 Square:100 (行为被改变了!)
}
✅ 遵循 LSP 的例子
// 使用接口而不是继承
public interface Shape {
int getArea();
}
public class Rectangle implements Shape {
private int width;
private int height;
public void setWidth(int width) { this.width = width; }
public void setHeight(int height) { this.height = height; }
public int getArea() { return width * height; }
}
public class Square implements Shape {
private int side;
public void setSide(int side) { this.side = side; }
public int getArea() { return side * side; }
}
4. I - 接口隔离原则(Interface Segregation Principle, ISP)
核心思想
客户端不应该依赖它不需要的接口
多个特定的接口比一个通用的接口更好
❌ 违反 ISP 的例子
// 臃肿的接口
public interface Worker {
void work();
void eat();
void sleep();
void attendMeeting();
void writeReport();
}
// 机器人不需要吃饭和睡觉,但被迫实现
public class Robot implements Worker {
public void work() { /* 工作 */ }
public void eat() { throw new UnsupportedOperationException(); }
public void sleep() { throw new UnsupportedOperationException(); }
public void attendMeeting() { /* 参加会议 */ }
public void writeReport() { /* 写报告 */ }
}
✅ 遵循 ISP 的例子
// 拆分成多个小接口
public interface Workable {
void work();
}
public interface Eatable {
void eat();
}
public interface Sleepable {
void sleep();
}
// 人类实现需要的接口
public class Human implements Workable, Eatable, Sleepable {
public void work() { /* 工作 */ }
public void eat() { /* 吃饭 */ }
public void sleep() { /* 睡觉 */ }
}
// 机器人只实现需要的接口
public class Robot implements Workable {
public void work() { /* 工作 */ }
}
5. D - 依赖倒置原则(Dependency Inversion Principle, DIP)
这个原则我刚才已经详细介绍过了,这里简要回顾:
核心思想
- 高层模块不应该依赖低层模块,两者都应该依赖抽象
- 抽象不应该依赖细节,细节应该依赖抽象
关键点
- 面向接口编程,而不是面向实现编程
- 扩展时只需修改配置/工厂,不需要改动业务代码
SOLID 原则总结
| 原则 | 缩写 | 一句话总结 | 解决的问题 |
|---|---|---|---|
| 单一职责原则 | SRP | 一个类只做一件事 | 类过于臃肿,难以维护 |
| 开闭原则 | OCP | 对扩展开放,对修改关闭 | 频繁修改稳定代码导致 Bug |
| 里氏替换原则 | LSP | 子类能无缝替换父类 | 继承导致的行为异常 |
| 接口隔离原则 | ISP | 接口要小而专,不要大而全 | 被迫实现不需要的方法 |
| 依赖倒置原则 | DIP | 依赖抽象,不依赖具体实现 | 代码耦合度高,难以扩展 |
SOLID 原则之间的关系
SRP(单一职责) → 指导如何拆分类
↓
OCP(开闭原则) → 拆分后如何扩展
↓
LSP(里氏替换) → 继承时如何保证正确性
↓
ISP(接口隔离) → 如何设计接口
↓
DIP(依赖倒置) → 如何降低模块间耦合
共同目标: 创建高内聚、低耦合、易维护、易扩展的软件系统