一、什么是设计模式?
设计模式是软件开发中针对常见设计问题总结出来的一套可复用解决方案。它不是具体的代码模板,而是一种经过实践验证的设计思路。
在 Java 开发中,设计模式常用于解决以下问题:
- 如何创建对象更灵活
- 如何降低类与类之间的耦合
- 如何让代码更容易扩展
- 如何复用已有代码结构
- 如何让复杂业务逻辑更清晰
设计模式本质上服务于一个目标:让代码在变化面前更加稳定、清晰、可维护。
二、设计模式与面向对象原则
学习设计模式之前,需要理解几个重要的面向对象设计原则。
1. 单一职责原则
一个类应该只负责一类事情。
例如,订单类只负责订单数据和订单行为,不应该同时负责发送邮件、写日志、生成报表。
java
public class OrderService {
public void createOrder() {
// 创建订单
}
}
public class EmailService {
public void sendEmail() {
// 发送邮件
}
}
这样做的好处是,当邮件逻辑变化时,不会影响订单逻辑。
2. 开闭原则
软件实体应该对扩展开放,对修改关闭。
也就是说,当需求变化时,尽量通过新增代码完成,而不是频繁修改已有代码。
java
public interface PayStrategy {
void pay();
}
public class AliPayStrategy implements PayStrategy {
public void pay() {
System.out.println("支付宝支付");
}
}
public class WeChatPayStrategy implements PayStrategy {
public void pay() {
System.out.println("微信支付");
}
}
如果后续增加银行卡支付,只需要新增一个实现类,不需要修改原有支付逻辑。
3. 里氏替换原则
子类对象应该可以替换父类对象,并且程序行为保持正确。
简单来说,继承不能破坏父类原有的语义。
4. 接口隔离原则
客户端不应该依赖它不需要的接口。
不要设计过于庞大的接口,应当拆分成更小、更明确的接口。
5. 依赖倒置原则
高层模块不应该依赖低层模块,二者都应该依赖抽象。
java
public class OrderService {
private final PayStrategy payStrategy;
public OrderService(PayStrategy payStrategy) {
this.payStrategy = payStrategy;
}
public void pay() {
payStrategy.pay();
}
}
这里 OrderService 依赖的是 PayStrategy 抽象,而不是具体的支付宝或微信支付类。
三、设计模式的分类
经典的 GoF 设计模式一共有 23 种,通常分为三大类:
| 类型 | 作用 |
|---|---|
| 创建型模式 | 解决对象如何创建的问题 |
| 结构型模式 | 解决类或对象如何组合的问题 |
| 行为型模式 | 解决对象之间如何协作的问题 |
四、创建型模式
创建型模式关注对象创建过程,目的是让对象创建更加灵活。
1. 单例模式
单例模式保证一个类在 JVM 中只有一个实例,并提供全局访问点。
常见场景:
- 配置管理器
- 连接池
- 日志对象
- Spring 中默认的 Bean 单例
java
public class Singleton {
private static volatile Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
这里使用了双重检查锁,并通过 volatile 防止指令重排。
优点:
- 节省资源
- 全局唯一
- 访问方便
缺点:
- 容易隐藏依赖关系
- 在测试中不太方便替换
- 滥用会导致全局状态过多
2. 工厂方法模式
工厂方法模式把对象创建逻辑交给子类或具体工厂处理。
java
public interface Product {
void use();
}
public class Phone implements Product {
public void use() {
System.out.println("使用手机");
}
}
public class Computer implements Product {
public void use() {
System.out.println("使用电脑");
}
}
public interface ProductFactory {
Product createProduct();
}
public class PhoneFactory implements ProductFactory {
public Product createProduct() {
return new Phone();
}
}
public class ComputerFactory implements ProductFactory {
public Product createProduct() {
return new Computer();
}
}
适用场景:
- 创建逻辑复杂
- 具体类型经常扩展
- 调用方不希望关心对象创建细节
3. 抽象工厂模式
抽象工厂用于创建一组相关对象。
例如,不同操作系统下的按钮和输入框:
java
public interface Button {
void render();
}
public interface TextBox {
void render();
}
public class WindowsButton implements Button {
public void render() {
System.out.println("Windows 按钮");
}
}
public class MacButton implements Button {
public void render() {
System.out.println("Mac 按钮");
}
}
public interface UIFactory {
Button createButton();
TextBox createTextBox();
}
抽象工厂适合产品族稳定、产品等级结构明确的场景。
4. 建造者模式
建造者模式用于一步步创建复杂对象。
java
public class User {
private String name;
private Integer age;
private String email;
private User(Builder builder) {
this.name = builder.name;
this.age = builder.age;
this.email = builder.email;
}
public static class Builder {
private String name;
private Integer age;
private String email;
public Builder name(String name) {
this.name = name;
return this;
}
public Builder age(Integer age) {
this.age = age;
return this;
}
public Builder email(String email) {
this.email = email;
return this;
}
public User build() {
return new User(this);
}
}
}
使用方式:
java
User user = new User.Builder()
.name("Tom")
.age(18)
.email("tom@example.com")
.build();
适用场景:
- 构造参数很多
- 参数可选
- 对象创建过程需要更强可读性
5. 原型模式
原型模式通过复制已有对象来创建新对象。
java
public class Resume implements Cloneable {
private String name;
public Resume(String name) {
this.name = name;
}
public Resume clone() {
try {
return (Resume) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
}
适用场景:
- 对象创建成本较高
- 需要大量相似对象
- 希望保留对象当前状态后再复制
需要注意浅拷贝和深拷贝的区别。
五、结构型模式
结构型模式关注类和对象之间的组合关系。
1. 适配器模式
适配器模式用于让原本接口不兼容的类可以一起工作。
java
public interface MediaPlayer {
void play(String fileName);
}
public class Mp4Player {
public void playMp4(String fileName) {
System.out.println("播放 mp4:" + fileName);
}
}
public class Mp4Adapter implements MediaPlayer {
private final Mp4Player mp4Player = new Mp4Player();
public void play(String fileName) {
mp4Player.playMp4(fileName);
}
}
适用场景:
- 接入第三方接口
- 老系统兼容新接口
- 不想修改已有类
2. 装饰器模式
装饰器模式用于在不修改原类的情况下动态增强功能。
java
public interface Coffee {
String getName();
double cost();
}
public class SimpleCoffee implements Coffee {
public String getName() {
return "普通咖啡";
}
public double cost() {
return 10;
}
}
public class MilkDecorator implements Coffee {
private final Coffee coffee;
public MilkDecorator(Coffee coffee) {
this.coffee = coffee;
}
public String getName() {
return coffee.getName() + " + 牛奶";
}
public double cost() {
return coffee.cost() + 3;
}
}
使用方式:
java
Coffee coffee = new MilkDecorator(new SimpleCoffee());
System.out.println(coffee.getName());
System.out.println(coffee.cost());
Java IO 中大量使用了装饰器模式,例如:
java
BufferedInputStream inputStream =
new BufferedInputStream(new FileInputStream("test.txt"));
3. 代理模式
代理模式通过代理对象控制对真实对象的访问。
java
public interface UserService {
void saveUser();
}
public class UserServiceImpl implements UserService {
public void saveUser() {
System.out.println("保存用户");
}
}
public class UserServiceProxy implements UserService {
private final UserService userService;
public UserServiceProxy(UserService userService) {
this.userService = userService;
}
public void saveUser() {
System.out.println("权限校验");
userService.saveUser();
System.out.println("记录日志");
}
}
常见场景:
- Spring AOP
- 事务管理
- 权限校验
- 远程调用
- 缓存代理
4. 外观模式
外观模式为复杂子系统提供统一入口。
java
public class OrderFacade {
private final StockService stockService = new StockService();
private final PayService payService = new PayService();
private final DeliveryService deliveryService = new DeliveryService();
public void createOrder() {
stockService.lockStock();
payService.pay();
deliveryService.delivery();
}
}
调用方只需要使用 OrderFacade,不需要了解库存、支付、配送等内部细节。
5. 组合模式
组合模式用于把对象组织成树形结构,并让客户端以统一方式处理单个对象和组合对象。
java
public interface MenuComponent {
void print();
}
public class MenuItem implements MenuComponent {
private final String name;
public MenuItem(String name) {
this.name = name;
}
public void print() {
System.out.println(name);
}
}
public class Menu implements MenuComponent {
private final List<MenuComponent> children = new ArrayList<>();
public void add(MenuComponent component) {
children.add(component);
}
public void print() {
for (MenuComponent child : children) {
child.print();
}
}
}
适用场景:
- 菜单
- 文件目录
- 组织架构
- 权限树
六、行为型模式
行为型模式关注对象之间的职责分配和协作方式。
1. 策略模式
策略模式定义一组算法,并让它们可以相互替换。
java
public interface DiscountStrategy {
BigDecimal discount(BigDecimal amount);
}
public class NewUserDiscount implements DiscountStrategy {
public BigDecimal discount(BigDecimal amount) {
return amount.multiply(new BigDecimal("0.8"));
}
}
public class VipDiscount implements DiscountStrategy {
public BigDecimal discount(BigDecimal amount) {
return amount.multiply(new BigDecimal("0.7"));
}
}
public class OrderService {
private final DiscountStrategy discountStrategy;
public OrderService(DiscountStrategy discountStrategy) {
this.discountStrategy = discountStrategy;
}
public BigDecimal calculate(BigDecimal amount) {
return discountStrategy.discount(amount);
}
}
适用场景:
- 支付方式
- 优惠规则
- 排序算法
- 风控规则
- 文件导出格式
策略模式通常可以替代大量 if else。
2. 模板方法模式
模板方法模式在父类中定义算法骨架,把部分步骤交给子类实现。
java
public abstract class AbstractOrderProcessor {
public final void process() {
validate();
pay();
delivery();
}
protected void validate() {
System.out.println("校验订单");
}
protected abstract void pay();
protected void delivery() {
System.out.println("发货");
}
}
public class OnlineOrderProcessor extends AbstractOrderProcessor {
protected void pay() {
System.out.println("在线支付");
}
}
适用场景:
- 流程固定
- 某些步骤允许变化
- 多个类存在相似处理流程
3. 观察者模式
观察者模式用于对象状态变化时通知多个观察者。
java
public interface Observer {
void update(String message);
}
public class EmailObserver implements Observer {
public void update(String message) {
System.out.println("发送邮件:" + message);
}
}
public class SmsObserver implements Observer {
public void update(String message) {
System.out.println("发送短信:" + message);
}
}
public class Subject {
private final List<Observer> observers = new ArrayList<>();
public void addObserver(Observer observer) {
observers.add(observer);
}
public void notifyObservers(String message) {
for (Observer observer : observers) {
observer.update(message);
}
}
}
常见场景:
- 事件监听
- 消息通知
- Spring ApplicationEvent
- GUI 事件机制
4. 责任链模式
责任链模式让多个处理器按顺序处理请求。
java
public abstract class Handler {
protected Handler next;
public void setNext(Handler next) {
this.next = next;
}
public void handle(String request) {
doHandle(request);
if (next != null) {
next.handle(request);
}
}
protected abstract void doHandle(String request);
}
public class AuthHandler extends Handler {
protected void doHandle(String request) {
System.out.println("权限校验");
}
}
public class LogHandler extends Handler {
protected void doHandle(String request) {
System.out.println("记录日志");
}
}
适用场景:
- Servlet Filter
- Spring Security Filter Chain
- 参数校验链
- 风控规则链
- 审批流程
5. 命令模式
命令模式把请求封装成对象,从而支持排队、撤销、日志等能力。
java
public interface Command {
void execute();
}
public class Light {
public void on() {
System.out.println("开灯");
}
}
public class LightOnCommand implements Command {
private final Light light;
public LightOnCommand(Light light) {
this.light = light;
}
public void execute() {
light.on();
}
}
public class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
}
适用场景:
- 操作排队
- 任务调度
- 撤销重做
- MQ 消息任务封装
6. 状态模式
状态模式把对象在不同状态下的行为封装到不同状态类中。
java
public interface OrderState {
void handle();
}
public class PendingState implements OrderState {
public void handle() {
System.out.println("待支付订单,执行支付逻辑");
}
}
public class PaidState implements OrderState {
public void handle() {
System.out.println("已支付订单,执行发货逻辑");
}
}
public class OrderContext {
private OrderState state;
public void setState(OrderState state) {
this.state = state;
}
public void handle() {
state.handle();
}
}
适用场景:
- 订单状态流转
- 工单状态流转
- 审批状态机
- 游戏角色状态
当状态很多,并且每种状态行为差异明显时,状态模式比大量 switch 更清晰。
七、Java 开发中常见设计模式案例
1. Spring 中的设计模式
Spring 框架大量使用设计模式:
| 场景 | 设计模式 |
|---|---|
| Bean 默认单例 | 单例模式 |
| BeanFactory | 工厂模式 |
| FactoryBean | 工厂方法模式 |
| AOP | 代理模式 |
| ApplicationEvent | 观察者模式 |
| JdbcTemplate | 模板方法模式 |
| HandlerInterceptor | 责任链思想 |
| BeanPostProcessor | 扩展点模式 |
例如 Spring AOP 本质上就是代理模式:
java
@Transactional
public void createOrder() {
// 业务逻辑
}
Spring 会为这个 Bean 创建代理对象,在方法执行前后织入事务逻辑。
2. JDK 中的设计模式
| JDK 类 | 设计模式 |
|---|---|
Runtime.getRuntime() |
单例模式 |
Calendar.getInstance() |
工厂模式 |
InputStream / BufferedInputStream |
装饰器模式 |
Proxy |
动态代理模式 |
ThreadPoolExecutor |
策略思想 |
Observer |
观察者模式 |
Collections.unmodifiableList() |
代理或装饰思想 |
八、设计模式不是越多越好
设计模式的价值在于解决问题,而不是炫技。
如果业务逻辑很简单,强行引入模式反而会增加复杂度。例如一个简单的判断逻辑,本来几行 if else 就能解决,没有必要立刻抽象成策略模式。
设计模式应该在这些情况下使用:
- 代码已经出现明显重复
- 分支逻辑持续膨胀
- 业务规则经常变化
- 对象创建过程越来越复杂
- 模块之间耦合越来越严重
- 需要统一扩展点
不应该在这些情况下使用:
- 只是为了看起来高级
- 需求还不稳定,过早抽象
- 简单逻辑被拆成大量类
- 团队成员难以理解维护
九、如何在实际项目中选择设计模式?
可以按照问题类型来判断。
1. 对象创建复杂
优先考虑:
- 工厂方法模式
- 抽象工厂模式
- 建造者模式
- 单例模式
2. 多个算法可以切换
优先考虑:
- 策略模式
- 模板方法模式
3. 多个处理步骤组成链路
优先考虑:
- 责任链模式
- 模板方法模式
4. 需要增强已有对象能力
优先考虑:
- 装饰器模式
- 代理模式
5. 一个事件触发多个动作
优先考虑:
- 观察者模式
- 发布订阅模型
6. 状态不同,行为不同
优先考虑:
- 状态模式
十、总结
Java 设计模式不是死记硬背 23 种模式,而是理解背后的设计思想。
创建型模式解决"对象怎么创建"的问题,结构型模式解决"对象怎么组合"的问题,行为型模式解决"对象怎么协作"的问题。
在实际开发中,最常用的设计模式包括:
- 单例模式
- 工厂模式
- 建造者模式
- 代理模式
- 装饰器模式
- 策略模式
- 模板方法模式
- 观察者模式
- 责任链模式
- 状态模式
真正优秀的代码不是模式堆砌出来的,而是在合适的场景下,用合适的抽象解决真实的复杂度。
设计模式的最佳学习方式是结合源码和项目场景:
- 看 Spring 如何使用代理、工厂和模板方法
- 看 JDK IO 如何使用装饰器
- 看业务系统如何用策略模式消除复杂分支
- 看过滤器、拦截器如何体现责任链思想
掌握设计模式之后,写代码时就不只是"实现功能",而是会主动思考代码的扩展性、可维护性和长期演进成本。