设计模式的 SOLID 原则 及其他重要原则,每个原则都会配具体的代码示例和解释。
🎯 1. 单一职责原则 (SRP)
原则:一个类应该只有一个引起变化的原因(即只负责一个功能)。
⚠️ 违反 SRP 的代码示例
// 违反 SRP:User 类既管理用户信息,又处理数据库操作
class User {
private String name;
public User(String name) {
this.name = name;
}
public void saveToDatabase() {
System.out.println("Saving " + name + " to database..."); // 混合业务逻辑和持久化逻辑
}
}
问题:如果数据库操作逻辑变化(如改用 NoSQL),必须修改 User 类,违背 SRP。
✅ 遵守 SRP 的改进代码
// User 类只负责存储用户数据
class User {
private String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
// UserRepository 类专门负责数据库操作
class UserRepository {
public void saveToDatabase(User user) {
System.out.println("Saving " + user.getName() + " to database...");
}
}
改进点:
User只管理用户数据。UserRepository只处理数据库操作。
🎯 2. 开闭原则 (OCP)
原则:对扩展开放,对修改关闭(新增功能时不修改已有代码)。
⚠️ 违反 OCP 的代码示例
// 违反 OCP:新增支付方式需要修改 PaymentProcessor
class PaymentProcessor {
public void processPayment(String paymentType) {
if ("alipay".equals(paymentType)) {
System.out.println("Processing Alipay...");
} else if ("wechat".equals(paymentType)) {
System.out.println("Processing WeChat Pay..."); // 新增支付方式必须修改代码
}
}
}
问题:每次新增支付方式都要修改 PaymentProcessor,违反 OCP。
✅ 遵守 OCP 的改进代码
// 定义支付接口
interface PaymentMethod {
void pay();
}
// 实现具体支付方式
class Alipay implements PaymentMethod {
@Override
public void pay() {
System.out.println("Processing Alipay...");
}
}
class WeChatPay implements PaymentMethod {
@Override
public void pay() {
System.out.println("Processing WeChat Pay...");
}
}
// PaymentProcessor 无需修改即可支持新支付方式
class PaymentProcessor {
public void processPayment(PaymentMethod payment) {
payment.pay();
}
}
改进点:
- 新增支付方式只需实现
PaymentMethod,无需修改PaymentProcessor。
🎯 3. 里氏替换原则 (LSP)
原则:子类必须能替换父类,且不影响程序正确性。
⚠️ 违反 LSP 的代码示例
// 长方形
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 calculateArea() {
return width * height;
}
}
// 正方形(继承长方形)
class Square extends Rectangle {
@Override
public void setWidth(int width) {
super.setWidth(width);
super.setHeight(width); // 强制宽高相同,破坏长方形行为
}
@Override
public void setHeight(int height) {
super.setWidth(height);
super.setHeight(height);
}
}
// 使用时可能出错
public class Main {
public static void main(String[] args) {
Rectangle rect = new Square();
rect.setWidth(5);
rect.setHeight(10); // 预期面积 50,实际是 100(违反 LSP)
System.out.println(rect.calculateArea());
}
}
问题:Square 修改了 Rectangle 的行为,导致计算结果错误。
✅ 遵守 LSP 的改进代码
// 使用抽象类或接口
abstract class Shape {
public abstract int calculateArea();
}
class Rectangle extends Shape {
private int width;
private int height;
// getters & setters...
@Override
public int calculateArea() {
return width * height;
}
}
class Square extends Shape {
private int side;
// getter & setter...
@Override
public int calculateArea() {
return side * side;
}
}
改进点:
Square不再继承Rectangle,避免破坏行为。
🎯 4. 接口隔离原则 (ISP)
原则:客户端不应被迫依赖它不使用的接口。
⚠️ 违反 ISP 的代码示例
// 多功能接口(包含打印、扫描、传真)
interface MultiFunctionDevice {
void print();
void scan();
void fax(); // 普通打印机不需要传真
}
// 普通打印机被迫实现不需要的方法
class BasicPrinter implements MultiFunctionDevice {
@Override
public void print() {
System.out.println("Printing...");
}
@Override
public void scan() {
throw new UnsupportedOperationException(); // 违反 ISP
}
@Override
public void fax() {
throw new UnsupportedOperationException();
}
}
问题:BasicPrinter 被迫实现不需要的方法。
✅ 遵守 ISP 的改进代码
// 拆分为最小接口
interface Printer {
void print();
}
interface Scanner {
void scan();
}
interface Fax {
void fax();
}
// 普通打印机只需实现 Printer
class BasicPrinter implements Printer {
@Override
public void print() {
System.out.println("Printing...");
}
}
// 高级设备可实现多个接口
class AdvancedMachine implements Printer, Scanner, Fax {
// 实现所有方法...
}
改进点:按需实现接口,避免"胖接口"。
违反原则的的示例中被实现的接口明显不够抽象,也不适合被其他类继承 而改进后的示例中,接口被分为了多个,对于被依赖者来说非常方便、灵活、可以自由选择可依赖的抽象组合
🎯 5. 依赖倒置原则 (DIP)
原则:依赖抽象,而非具体实现。
⚠️ 违反 DIP 的代码示例
// 高层模块直接依赖低层模块
class EmailService {
public void sendEmail() {
System.out.println("Sending email...");
}
}
class Notification {
private EmailService emailService; // 直接依赖具体实现
public Notification() {
this.emailService = new EmailService();
}
public void alert() {
emailService.sendEmail();
}
}
问题:Notification 直接依赖 EmailService,难以扩展(如改用短信通知)。
✅ 遵守 DIP 的改进代码
// 定义抽象接口
interface MessageService {
void send();
}
// 实现具体服务
class EmailService implements MessageService {
@Override
public void send() {
System.out.println("Sending email...");
}
}
class SMSService implements MessageService {
@Override
public void send() {
System.out.println("Sending SMS...");
}
}
// 高层模块依赖抽象
class Notification {
private MessageService messageService;
// 通过构造函数注入依赖(依赖注入)
public Notification(MessageService messageService) {
this.messageService = messageService;
}
public void alert() {
messageService.send();
}
}
// 使用时灵活替换实现
public class Main {
public static void main(String[] args) {
Notification emailNotif = new Notification(new EmailService());
Notification smsNotif = new Notification(new SMSService());
emailNotif.alert(); // Sending email...
smsNotif.alert(); // Sending SMS...
}
}
改进点:
Notification依赖MessageService接口,而非具体实现。- 通过 依赖注入(DI) 动态替换实现。
🎯 其他重要原则
6. 组合优于继承
// 不推荐:使用继承
class Bird {
void fly() { System.out.println("Flying..."); }
}
class Penguin extends Bird {
// 企鹅不会飞,但继承了 fly() 方法,违反 LSP
}
// 推荐:使用组合
interface Flyable {
void fly();
}
class Bird {
private Flyable flyBehavior;
public Bird(Flyable flyBehavior) {
this.flyBehavior = flyBehavior;
}
void performFly() {
flyBehavior.fly();
}
}
class NoFly implements Flyable {
@Override
public void fly() {
System.out.println("Cannot fly!");
}
}
// 使用时动态组合
Bird penguin = new Bird(new NoFly());
penguin.performFly(); // Cannot fly!
7. 迪米特法则(最少知识原则)
// 违反:直接访问深层对象
class House {
private Kitchen kitchen;
public Sink getKitchenSink() {
return kitchen.getSink(); // 暴露了 Kitchen 的内部细节
}
}
// 遵守:封装间接访问
class House {
private Kitchen kitchen;
public void getWater() {
kitchen.provideWater(); // 只暴露必要方法
}
}
📜 总结表
| 原则 | 关键思想 | Java 实现关键点 |
|---|---|---|
| SRP | 单一职责 | 拆分类的功能(如 User 和 UserRepository) |
| OCP | 开闭原则 | 使用接口 + 多态(如 PaymentMethod) |
| LSP | 里氏替换 | 子类不破坏父类行为(如 Shape 抽象类) |
| ISP | 接口隔离 | 拆分为最小接口(如 Printer、Scanner) |
| DIP | 依赖倒置 | 依赖抽象 + 依赖注入(如 MessageService) |
这些原则共同目标是:提高代码的可维护性、扩展性和复用性。实际开发中需要灵活权衡,而非教条式遵循。