SOLID 原则
SOLID 是面向对象设计的五个基本原则,是软件工程中经过时间检验的设计指导思想。
五大原则一览
| 原则 | 全称 | 一句话概括 |
|---|---|---|
| S | Single Responsibility Principle | 单一职责:一个类只做一件事 |
| O | Open/Closed Principle | 开闭原则:对扩展开放,对修改关闭 |
| L | Liskov Substitution Principle | 里氏替换:子类可以替换父类 |
| I | Interface Segregation Principle | 接口隔离:接口要小而专 |
| D | Dependency Inversion Principle | 依赖倒置:依赖抽象,不依赖具体 |
1. 单一职责原则(SRP)
一个类只有一个引起它变化的原因。
java
// ❌ 违反 SRP:同时负责多个职责
class User {
void saveUser() {} // 数据存储
void sendEmail() {} // 发送邮件
void generateReport() {} // 生成报表
}
// ✅ 遵守 SRP:每个类一个职责
class UserRepository {
void saveUser() {}
}
class EmailService {
void sendEmail() {}
}
class ReportGenerator {
void generateReport() {}
}2. 开闭原则(OCP)
软件实体应该对扩展开放,对修改关闭。
java
// ❌ 违反 OCP:修改现有代码添加新功能
class AreaCalculator {
double calculate(Object shape) {
if (shape instanceof Square) {
// ...
} else if (shape instanceof Circle) {
// ...
}
// 每加一个形状就要改这里
return 0;
}
}
// ✅ 遵守 OCP:扩展新功能
interface Shape {
double area();
}
class Square implements Shape {
@Override
public double area() { return /* ... */; }
}
class Circle implements Shape {
@Override
public double area() { return /* ... */; }
}
// 新增形状只需要实现 Shape,不用改这里
class AreaCalculator {
double calculate(Shape shape) {
return shape.area(); // 不用改!
}
}3. 里氏替换原则(LSP)
子类必须能够替换其基类。
java
// 经典的正方形/矩形问题
class Rectangle {
protected int width, height;
public void setWidth(int width) { this.width = width; }
public void setHeight(int height) { this.height = height; }
public int area() { return width * height; }
}
class Square extends Rectangle {
@Override
public void setWidth(int width) {
this.width = width;
this.height = width; // 违反 LSP!
}
@Override
public void setHeight(int height) {
this.width = height;
this.height = height;
}
}
// 如果一个方法期望 Rectangle,你会得到意外结果
void resize(Rectangle r) {
r.setWidth(5);
r.setHeight(4);
// 期望面积 20,正方形却是 16
}4. 接口隔离原则(ISP)
客户端不应该依赖它不需要的接口。
java
// ❌ 违反 ISP:大而全的接口
interface Machine {
void print();
void scan();
void fax();
}
// 打印机必须实现 scan 和 fax,即使用不到
class SimplePrinter implements Machine {
public void print() { }
public void scan() { throw new UnsupportedOperationException(); }
public void fax() { throw new UnsupportedOperationException(); }
}
// ✅ 遵守 ISP:小而专的接口
interface Printer { void print(); }
interface Scanner { void scan(); }
interface Fax { void fax(); }
// 每个类只实现需要的接口
class SimplePrinter implements Printer {
public void print() { }
}
class AllInOnePrinter implements Printer, Scanner, Fax {
public void print() { }
public void scan() { }
public void fax() { }
}5. 依赖倒置原则(DIP)
高层模块不应该依赖低层模块,两者都应该依赖抽象。
java
// ❌ 违反 DIP:高层依赖低层
class OrderService {
private MySQLRepository repository; // 直接依赖具体实现
public void save(Order order) {
repository.save(order);
}
}
// ✅ 遵守 DIP:依赖抽象
interface Repository {
void save(Order order);
}
class OrderService {
private final Repository repository; // 依赖抽象
public OrderService(Repository repository) {
this.repository = repository;
}
public void save(Order order) {
repository.save(order);
}
}
class MySQLRepository implements Repository {
public void save(Order order) { /* ... */ }
}
class MongoDBRepository implements Repository {
public void save(Order order) { /* ... */ }
}实际应用:重构示例
重构前
java
// 一个类做了太多事情
class UserManager {
private DatabaseConnection db;
void createUser(User user) {
// 验证
if (user.getEmail() == null) throw new Exception("Email required");
// 保存到数据库
db.execute("INSERT ...");
// 发送欢迎邮件
emailService.send(user.getEmail(), "Welcome");
// 记录日志
logger.info("User created: " + user.getEmail());
}
}重构后
java
// 单一职责,每个类只做一件事
class UserValidator {
void validate(User user) {
if (user.getEmail() == null) {
throw new ValidationException("Email required");
}
}
}
interface UserRepository {
void save(User user);
}
interface NotificationService {
void notifyUserCreated(User user);
}
interface AuditLogger {
void logUserCreation(User user);
}
class UserService {
private final UserValidator validator;
private final UserRepository repository;
private final NotificationService notifier;
private final AuditLogger logger;
public UserService(UserValidator validator,
UserRepository repository,
NotificationService notifier,
AuditLogger logger) {
this.validator = validator;
this.repository = repository;
this.notifier = notifier;
this.logger = logger;
}
public void createUser(User user) {
validator.validate(user); // 验证
repository.save(user); // 保存
notifier.notifyUserCreated(user); // 通知
logger.logUserCreation(user); // 日志
}
}SOLID 之间的关系
依赖倒置原则(DIP)
↓
开闭原则(OCP)
↓
里氏替换原则(LSP)
↓
单一职责原则(SRP)
↓
接口隔离原则(ISP)SOLID 原则不是孤立的,它们相互关联、相互支撑。理解这些原则背后的思想,比死记硬背更重要。
总结
| 原则 | 核心思想 | 反面典型 |
|---|---|---|
| S | 职责单一 | 上帝类 |
| O | 扩展开放,修改关闭 | 无限修改 |
| L | 子类替换父类 | 菱形继承 |
| I | 接口小而专 | 大而全接口 |
| D | 依赖抽象 | 依赖具体 |
记住:好的设计是能够经受变化考验的设计。SOLID 原则帮助我们构建这样的系统。
