JAVA面向对象设计的五大原则

SOLID 五大设计原则(附 Java 代码示例)

SOLID 是五个原则的首字母缩写,分别对应:

  • S:单一职责原则(Single Responsibility Principle, SRP)
  • O:开闭原则(Open/Closed Principle, OCP)
  • L:里氏替换原则(Liskov Substitution Principle, LSP)
  • I:接口隔离原则(Interface Segregation Principle, ISP)
  • D:依赖倒置原则(Dependency Inversion Principle, DIP)

1. S - 单一职责原则(SRP)

核心定义

一个类 / 方法只负责一个功能职责,修改它的原因只有一个。

通俗解释

就像 "厨师" 只负责做饭,"服务员" 只负责上菜 ------ 如果一个类又做饭又上菜,改做饭逻辑时可能会影响上菜功能,代码易出问题。

反例(坏代码)
复制代码
// 一个类同时负责"用户信息管理"和"用户日志记录",违反SRP
public class UserService {
    // 职责1:管理用户信息
    public void saveUser(String username) {
        System.out.println("保存用户:" + username);
    }
    
    // 职责2:记录日志(和用户管理无关)
    public void logUserOperation(String username, String operation) {
        System.out.println("日志:" + username + "执行了" + operation);
    }
}
正例(好代码)
复制代码
// 职责1:仅管理用户信息
public class UserService {
    public void saveUser(String username) {
        System.out.println("保存用户:" + username);
    }
}

// 职责2:仅记录日志
public class LogService {
    public void logOperation(String username, String operation) {
        System.out.println("日志:" + username + "执行了" + operation);
    }
}

// 使用示例
public class Demo {
    public static void main(String[] args) {
        UserService userService = new UserService();
        LogService logService = new LogService();
        
        userService.saveUser("张三");
        logService.logOperation("张三", "保存用户");
    }
}
核心价值

降低代码耦合度,修改一个功能时不会影响其他功能,提高可维护性。

2. O - 开闭原则(OCP)

核心定义

软件实体(类、方法、模块)对扩展开放,对修改关闭。

通俗解释

新增功能时,不用改原有代码,而是通过 "扩展"(比如新增子类、实现接口)来实现 ------ 就像给手机装新 APP,不用拆手机硬件。

反例(坏代码)
复制代码
// 新增"矩形"时,必须修改原有方法,违反OCP
public class ShapeCalculator {
    public double calculateArea(Object shape) {
        if (shape instanceof Circle) {
            Circle circle = (Circle) shape;
            return Math.PI * circle.getRadius() * circle.getRadius();
        } 
        // 新增矩形时,必须加else if,修改原有代码
        else if (shape instanceof Rectangle) {
            Rectangle rect = (Rectangle) shape;
            return rect.getWidth() * rect.getHeight();
        }
        return 0;
    }
}
正例(好代码)
复制代码
// 抽象父类(定义规范)
public abstract class Shape {
    public abstract double getArea();
}

// 圆形(扩展实现)
public class Circle extends Shape {
    private double radius;
    public Circle(double radius) { this.radius = radius; }
    @Override
    public double getArea() {
        return Math.PI * radius * radius;
    }
}

// 矩形(扩展实现,无需修改原有代码)
public class Rectangle extends Shape {
    private double width, height;
    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }
    @Override
    public double getArea() {
        return width * height;
    }
}

// 计算器(无需修改,对扩展开放)
public class ShapeCalculator {
    public double calculateArea(Shape shape) {
        return shape.getArea(); // 多态调用子类方法
    }
}

// 使用示例
public class Demo {
    public static void main(String[] args) {
        ShapeCalculator calculator = new ShapeCalculator();
        // 新增三角形时,只需加Triangle类,无需改Calculator
        System.out.println(calculator.calculateArea(new Circle(5))); // 78.54
        System.out.println(calculator.calculateArea(new Rectangle(4,5))); // 20
    }
}
核心价值

原有代码是经过测试的,扩展不修改原有代码,能避免引入新 BUG。

3. L - 里氏替换原则(LSP)

核心定义

子类可以完全替换父类,且不会影响程序的正确性。

通俗解释

"猫" 是 "动物" 的子类,那么任何使用 "动物" 的地方,换成 "猫" 都能正常运行 ------ 就像 "塑料杯" 替换 "玻璃杯" 装水,功能不受影响。

反例(坏代码)
复制代码
// 正方形继承矩形,但替换后逻辑出错(违反LSP)
public class Rectangle {
    protected double width;
    protected double height;
    
    public void setWidth(double width) { this.width = width; }
    public void setHeight(double height) { this.height = height; }
    public double getArea() { return width * height; }
}

// 正方形的宽高必须相等,setWidth/setHeight逻辑冲突
public class Square extends Rectangle {
    @Override
    public void setWidth(double width) {
        super.setWidth(width);
        super.setHeight(width); // 强制同步高度
    }
    
    @Override
    public void setHeight(double height) {
        super.setHeight(height);
        super.setWidth(height); // 强制同步宽度
    }
}

// 测试:替换后逻辑出错
public class Demo {
    public static void resize(Rectangle rect) {
        rect.setWidth(10);
        rect.setHeight(20);
        // 预期面积200,但正方形替换后面积是400,违反LSP
        System.out.println("面积:" + rect.getArea()); 
    }
    
    public static void main(String[] args) {
        resize(new Square()); // 输出400,不符合预期
    }
}
正例(好代码)
复制代码
// 抽象父类:不定义宽高set方法,避免子类冲突
public abstract class Shape {
    public abstract double getArea();
}

// 矩形
public class Rectangle extends Shape {
    private double width, height;
    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }
    @Override
    public double getArea() {
        return width * height;
    }
}

// 正方形(独立实现,不继承矩形)
public class Square extends Shape {
    private double side;
    public Square(double side) { this.side = side; }
    @Override
    public double getArea() {
        return side * side;
    }
}

// 测试:替换后逻辑正确
public class Demo {
    public static void printArea(Shape shape) {
        System.out.println("面积:" + shape.getArea());
    }
    
    public static void main(String[] args) {
        printArea(new Rectangle(10,20)); // 200
        printArea(new Square(20)); // 400(符合预期)
    }
}
核心价值

保证继承体系的合理性,避免子类替换父类后出现逻辑异常。

4. I - 接口隔离原则(ISP)

核心定义

客户端不应该依赖它不需要的接口 ------ 拆分大接口为多个小接口,按需实现。

通俗解释

就像 "遥控器":电视遥控器不用包含空调的按键,每个设备的遥控器只保留自己需要的功能。

反例(坏代码)
复制代码
// 大接口:包含了打印机、扫描仪、传真机的所有方法
public interface Machine {
    void print(); // 打印
    void scan();  // 扫描
    void fax();   // 传真
}

// 普通打印机只需要print,但必须实现所有方法(包括不需要的scan/fax)
public class SimplePrinter implements Machine {
    @Override
    public void print() { System.out.println("打印文档"); }
    
    // 无用的实现,违反ISP
    @Override
    public void scan() { throw new UnsupportedOperationException("不支持扫描"); }
    @Override
    public void fax() { throw new UnsupportedOperationException("不支持传真"); }
}
正例(好代码)
复制代码
// 拆分小接口:按需定义
public interface Printable {
    void print();
}

public interface Scannable {
    void scan();
}

public interface Faxable {
    void fax();
}

// 普通打印机:只实现需要的接口
public class SimplePrinter implements Printable {
    @Override
    public void print() { System.out.println("打印文档"); }
}

// 多功能一体机:实现所有接口
public class MultiFunctionMachine implements Printable, Scannable, Faxable {
    @Override
    public void print() { System.out.println("打印文档"); }
    @Override
    public void scan() { System.out.println("扫描文档"); }
    @Override
    public void fax() { System.out.println("传真文档"); }
}
核心价值

避免类实现无用的方法,降低接口和实现类的耦合度。

5. D - 依赖倒置原则(DIP)

核心定义

高层模块不依赖低层模块,两者都依赖抽象;

抽象不依赖细节,细节依赖抽象。

通俗解释

程序员(高层)不依赖 "具体的键盘品牌"(低层),而是依赖 "键盘接口"(抽象)------ 换键盘时,程序员的操作逻辑不变。

反例(坏代码)
复制代码
// 低层模块:具体的MySQL数据库
public class MySQLDatabase {
    public void connect() { System.out.println("连接MySQL"); }
}

// 高层模块:直接依赖低层的MySQL,换数据库时必须修改代码
public class UserRepository {
    private MySQLDatabase db = new MySQLDatabase(); // 硬依赖
    public void saveUser() {
        db.connect();
        System.out.println("保存用户到MySQL");
    }
}
正例(好代码)
复制代码
// 抽象接口:定义数据库规范
public interface Database {
    void connect();
}

// 低层模块1:MySQL实现
public class MySQLDatabase implements Database {
    @Override
    public void connect() { System.out.println("连接MySQL"); }
}

// 低层模块2:Oracle实现(扩展)
public class OracleDatabase implements Database {
    @Override
    public void connect() { System.out.println("连接Oracle"); }
}

// 高层模块:依赖抽象,不依赖具体实现
public class UserRepository {
    private Database db;
    // 构造注入:传入任意Database实现
    public UserRepository(Database db) { this.db = db; }
    
    public void saveUser() {
        db.connect();
        System.out.println("保存用户");
    }
}

// 使用示例
public class Demo {
    public static void main(String[] args) {
        // 换数据库只需改传入的实现,无需修改UserRepository
        UserRepository repo1 = new UserRepository(new MySQLDatabase());
        repo1.saveUser(); // 连接MySQL + 保存用户
        
        UserRepository repo2 = new UserRepository(new OracleDatabase());
        repo2.saveUser(); // 连接Oracle + 保存用户
    }
}
核心价值

降低高层和低层模块的耦合,便于替换底层实现(比如换数据库、换框架)。

总结

SOLID 五大原则的核心目标是让 Java 代码更易维护、易扩展、低耦合,关键要点:

  • 单一职责:一个类只做一件事,修改原因唯一;
  • 开闭原则:新增功能靠扩展,不修改原有代码;
  • 里氏替换:子类可完全替换父类,逻辑不异常;
  • 接口隔离:接口按需拆分,不强迫实现无用方法;
  • 依赖倒置:面向抽象编程,不依赖具体实现。
    这些原则不是 "必须严格遵守" 的规则,而是 "指导思想"------ 实际开发中需平衡灵活性和复杂度,核心是让代码符合 "高内聚、低耦合" 的设计目标。
相关推荐
于先生吖2 小时前
基于 Java 开发智慧社区系统:跑腿 + 家政 + 本地生活服务实战教程
java·开发语言·生活
小箌2 小时前
springboot_01
java·spring boot·后端
sunwenjian8862 小时前
Spring Cloud gateway 路由规则
java
panzer_maus2 小时前
死锁的产生与解决
java·开发语言
asom222 小时前
MVC vs DDD
java·mvc·ddd
凯子坚持 c2 小时前
基于C++构建DeepSeek大模型推理SDK:从架构设计到工程落地
java·数据库·c++
星河耀银海2 小时前
C++ 异常处理机制:异常捕获、自定义异常与实战应用
android·java·c++
longzhen9z2 小时前
SpringSecurity踢出指定用户
java
洛阳泰山2 小时前
开源智能体搭建平台MaxKB4j 技术文档
java·开源·llm·springboot·agent·rag·langchain4j