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 代码更易维护、易扩展、低耦合,关键要点:
- 单一职责:一个类只做一件事,修改原因唯一;
- 开闭原则:新增功能靠扩展,不修改原有代码;
- 里氏替换:子类可完全替换父类,逻辑不异常;
- 接口隔离:接口按需拆分,不强迫实现无用方法;
- 依赖倒置:面向抽象编程,不依赖具体实现。
这些原则不是 "必须严格遵守" 的规则,而是 "指导思想"------ 实际开发中需平衡灵活性和复杂度,核心是让代码符合 "高内聚、低耦合" 的设计目标。