设计模式SOLID原则

173 阅读5分钟

设计模式的 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单一职责拆分类的功能(如 UserUserRepository
OCP开闭原则使用接口 + 多态(如 PaymentMethod
LSP里氏替换子类不破坏父类行为(如 Shape 抽象类)
ISP接口隔离拆分为最小接口(如 PrinterScanner
DIP依赖倒置依赖抽象 + 依赖注入(如 MessageService

这些原则共同目标是:提高代码的可维护性、扩展性和复用性。实际开发中需要灵活权衡,而非教条式遵循。