SOLID设计原则
SOLID 是**面向对象设计(OOD)**中最经典的五大设计原则,由 Robert C. Martin(Uncle Bob)提出,也是 Java 面试中高频考点。
很多人会背 SOLID,但真正面试时,更重要的是结合实际项目说明如何应用。
SOLID 五大原则概览
| 缩写 | 英文 | 中文 | 核心思想 |
|---|---|---|---|
| S | Single Responsibility Principle | 单一职责原则 | 一个类只负责一件事 |
| O | Open-Closed Principle | 开闭原则 | 对扩展开放,对修改关闭 |
| L | Liskov Substitution Principle | 里氏替换原则 | 子类必须能够替换父类 |
| I | Interface Segregation Principle | 接口隔离原则 | 接口要小而专一 |
| D | Dependency Inversion Principle | 依赖倒置原则 | 面向接口编程,而不是面向实现 |
下面逐个讲解。
一、S —— 单一职责原则(SRP)
一个类应该只有一个引起它变化的原因。
也就是说:
一个类只负责一项职责。
错误设计
例如:
public class UserService {
public void login(){}
public void sendSms(){}
public void exportExcel(){}
public void uploadAvatar(){}
public void createOrder(){}
}
这个类负责:
- 登录
- 短信
- 导出
- 上传
- 下单
明显职责太多。
任何一个功能变化,都可能修改这个类。
正确设计
拆分:
LoginService
SmsService
ExportService
FileService
OrderService
每个类负责一种业务。
这样:
登录改了
不会影响
导出功能。
生产案例
例如:
电商系统:
OrderService
只负责:
创建订单
取消订单
查询订单
支付:
PayService
物流:
LogisticsService
库存:
StockService
这样维护起来非常方便。
二、O —— 开闭原则(OCP)
软件实体应该对扩展开放,对修改关闭。
什么意思?
新增功能:
尽量新增代码。
不要:
修改已有代码。
例如支付:
错误:
if(channel=="ALI"){
}
else if(channel=="WX"){
}
else if(channel=="PAYPAL"){
}
以后:
新增:
Stripe
又要改 if。
违反开闭原则。
正确:
interface PayService{
void pay();
}
支付宝:
class AlipayService
微信:
class WechatPayService
新增:
class PaypalService
OrderService
不用改。
Spring 中:
@Resource
private PayService payService;
或者:
Map<String, PayService>
都是典型应用。
三、L —— 里氏替换原则(LSP)
所有引用父类的地方,都必须能够透明地替换成子类。
这是多态成立的重要基础。
例如:
Animal animal = new Dog();
animal.eat();
为什么成立?
因为:
Dog
能够替换
Animal。
为什么 Java 重写有
两同两小一大
就是为了保证:
LSP。
例如:
父类:
public Animal get(){}
子类:
public Dog get(){}
允许。
因为:
Dog
就是
Animal。
但是:
public Object get(){}
不允许。
否则:
别人要 Animal。
你给 Object。
程序就错了。
经典反例
例如:
Bird
里面:
fly()
然后:
Penguin extends Bird
企鹅不会飞。
说明:
继承关系设计错了。
正确:
Bird
↓
FlyBird
↓
Penguin
或者:
Flyable
接口。
四、I —— 接口隔离原则(ISP)
客户端不应该依赖自己不需要的方法。
接口:
应该:
小。
专。
例如:
错误:
Animal{
eat();
run();
fly();
swim();
}
狗:
必须实现:
fly();
明显错误。
正确:
Flyable
Runnable
Swimmable
Eatable
Dog:
实现:
Runnable
Swimmable
Bird:
实现:
Flyable
Java 集合:
就是这样设计的。
Collection
↓
List
↓
Set
而不是:
一个超级接口。
五、D —— 依赖倒置原则(DIP)
高层模块不应该依赖低层模块,二者都应该依赖抽象。
也就是说:
不要:
new AlipayService();
而应该:
PayService
例如:
错误:
public class OrderService{
private AlipayService payService;
}
以后:
微信:
银联:
全部改。
正确:
public class OrderService{
private PayService payService;
}
Spring:
@Resource
private PayService payService;
真正运行:
注入:
AlipayService
WechatPayService
PaypalService
OrderService
完全不知道。
这就是:
IoC
DI
存在的意义。
五大原则之间的关系
SRP
│
│ 一个类只负责一种职责
│
├──────────────┐
│ │
▼ ▼
ISP OCP
接口更小 更容易扩展
│
└───────┐
▼
DIP
依赖抽象,不依赖实现
│
▼
LSP
子类可以安全替换父类
它们并不是彼此独立的,而是相互促进。例如,接口隔离(ISP)和依赖倒置(DIP)通常一起使用,而良好的单一职责(SRP)也会让系统更容易遵循开闭原则(OCP)。
Spring 框架中 SOLID 的体现
Spring 可以说是 SOLID 思想的经典实践:
| 原则 | Spring 中的体现 |
|---|---|
| SRP | Controller、Service、Repository 分层,各司其职 |
| OCP | 新增 Bean、AOP、扩展点(如 BeanPostProcessor)无需修改框架源码 |
| LSP | 父接口引用可以指向不同实现,如 DataSource、ApplicationContext |
| ISP | 提供大量小接口,如 BeanFactory、ResourceLoader、InitializingBean |
| DIP | IoC 容器和依赖注入(@Autowired、@Resource)让业务依赖抽象而不是具体实现 |
面试回答(推荐)
SOLID 是面向对象设计中的五大原则,包括:S(单一职责)、O(开闭原则)、L(里氏替换)、I(接口隔离) 和 D(依赖倒置)。单一职责强调一个类只负责一项职责;开闭原则强调通过新增代码扩展功能,而不是修改已有代码;里氏替换要求子类能够无缝替换父类,是 Java 多态和重写规则的基础;接口隔离要求接口小而专一,避免“胖接口”;依赖倒置要求业务依赖抽象而不是具体实现,这也是 Spring IoC 和依赖注入的核心思想。在实际项目中,例如支付、短信、文件存储等模块,我通常会先定义接口,再通过多个实现类配合 Spring 注入实现扩展,这样既符合开闭原则,也提高了系统的可维护性和可扩展性。
浙公网安备 33010602011771号