Appearance
设计模式
GoF(Gang of Four)23 种经典设计模式详解,聚焦 Java 开发中最常用的模式。
一、为什么学习设计模式
1.1 设计模式的价值
- 复用解决方案:避免重复发明轮子,直接使用经过验证的设计
- 统一沟通语言:团队中说"这里用策略模式"比描述具体实现更高效
- 面向对象设计原则的实践:模式是对 SOLID 原则的具体应用
- 提高代码可扩展性:好的模式使代码易于扩展,对修改封闭(开闭原则)
1.2 六大设计原则 (SOLID + 1)
| 原则 | 缩写 | 含义 |
|---|---|---|
| 单一职责原则 | SRP | 一个类只负责一件事 |
| 开闭原则 | OCP | 对扩展开放,对修改关闭 |
| 里氏替换原则 | LSP | 子类可以替换父类而不影响程序正确性 |
| 接口隔离原则 | ISP | 不应强迫客户端依赖它不需要的接口 |
| 依赖倒置原则 | DIP | 依赖抽象而非具体实现 |
| 迪米特法则 | LoD | 最少知识原则,减少类之间的耦合 |
1.3 设计模式分类
| 分类 | 模式 | 数量 |
|---|---|---|
| 创建型 | 单例、工厂方法、抽象工厂、建造者、原型 | 5 |
| 结构型 | 适配器、桥接、组合、装饰器、外观、享元、代理 | 7 |
| 行为型 | 责任链、命令、解释器、迭代器、中介者、备忘录、观察者、状态、策略、模板方法、访问者 | 11 |
二、创建型模式
2.1 单例模式 (Singleton)
意图:确保一个类只有一个实例,并提供全局访问点。
四种实现方式对比:
java
// 1. 饿汉式 -- 类加载时初始化,线程安全,但可能浪费内存
public class EagerSingleton {
private static final EagerSingleton INSTANCE = new EagerSingleton();
private EagerSingleton() {}
public static EagerSingleton getInstance() {
return INSTANCE;
}
}
// 2. 双重检查锁定 -- 懒加载,线程安全,volatile 防止指令重排
public class DoubleCheckSingleton {
private static volatile DoubleCheckSingleton instance;
private DoubleCheckSingleton() {}
public static DoubleCheckSingleton getInstance() {
if (instance == null) {
synchronized (DoubleCheckSingleton.class) {
if (instance == null) {
instance = new DoubleCheckSingleton();
}
}
}
return instance;
}
}
// 3. 静态内部类 -- 懒加载,线程安全,利用类加载机制
public class HolderSingleton {
private HolderSingleton() {}
private static class Holder {
private static final HolderSingleton INSTANCE = new HolderSingleton();
}
public static HolderSingleton getInstance() {
return Holder.INSTANCE;
}
}
// 4. 枚举 -- 最安全,防止反射和序列化破坏
public enum EnumSingleton {
INSTANCE;
public void doSomething() {
// 业务方法
}
}何时使用:全局配置管理、数据库连接池、日志工厂、Spring Bean(默认单例)。
面试要点:volatile 防止指令重排(new 对象不是原子操作:分配内存 -> 初始化 -> 赋值引用);枚举单例自动防止反射攻击和序列化破坏。
2.2 工厂方法模式 (Factory Method)
意图:定义一个创建对象的接口,让子类决定实例化哪个类。
java
// 产品接口
interface Product {
void use();
}
// 具体产品
class ConcreteProductA implements Product {
@Override
public void use() {
System.out.println("使用产品 A");
}
}
class ConcreteProductB implements Product {
@Override
public void use() {
System.out.println("使用产品 B");
}
}
// 工厂接口
interface Factory {
Product createProduct();
}
// 具体工厂
class FactoryA implements Factory {
@Override
public Product createProduct() {
return new ConcreteProductA();
}
}
class FactoryB implements Factory {
@Override
public Product createProduct() {
return new ConcreteProductB();
}
}
// 使用
Factory factory = new FactoryA();
Product product = factory.createProduct();
product.use();何时使用:当创建逻辑较复杂,或需要根据条件创建不同子类时。Spring 中的 FactoryBean 就是工厂方法模式的典型应用。
2.3 抽象工厂模式 (Abstract Factory)
意图:创建一系列相关或相互依赖的对象,无需指定具体类。
java
// 产品族:按钮和文本框
interface Button { void render(); }
interface TextField { void render(); }
// Windows 风格产品族
class WindowsButton implements Button {
@Override public void render() { System.out.println("Windows 按钮"); }
}
class WindowsTextField implements TextField {
@Override public void render() { System.out.println("Windows 文本框"); }
}
// Mac 风格产品族
class MacButton implements Button {
@Override public void render() { System.out.println("Mac 按钮"); }
}
class MacTextField implements TextField {
@Override public void render() { System.out.println("Mac 文本框"); }
}
// 抽象工厂
interface GUIFactory {
Button createButton();
TextField createTextField();
}
// 具体工厂 -- Windows 产品族
class WindowsFactory implements GUIFactory {
@Override public Button createButton() { return new WindowsButton(); }
@Override public TextField createTextField() { return new WindowsTextField(); }
}
// 具体工厂 -- Mac 产品族
class MacFactory implements GUIFactory {
@Override public Button createButton() { return new MacButton(); }
@Override public TextField createTextField() { return new MacTextField(); }
}与工厂方法模式的区别:工厂方法创建单个产品,抽象工厂创建产品族。抽象工厂 = 一组工厂方法。
2.4 建造者模式 (Builder)
意图:将复杂对象的构建与表示分离,使用相同的构建过程创建不同的表示。
java
@Data
public class Computer {
private String cpu;
private String memory;
private String disk;
private String gpu;
private String os;
// 私有构造函数
private Computer() {}
// 静态建造者
public static Builder builder() {
return new Builder();
}
public static class Builder {
private Computer computer = new Computer();
public Builder cpu(String cpu) {
computer.cpu = cpu;
return this;
}
public Builder memory(String memory) {
computer.memory = memory;
return this;
}
public Builder disk(String disk) {
computer.disk = disk;
return this;
}
public Builder gpu(String gpu) {
computer.gpu = gpu;
return this;
}
public Builder os(String os) {
computer.os = os;
return this;
}
public Computer build() {
// 可以在这里做校验
if (computer.cpu == null) {
throw new IllegalStateException("CPU 不能为空");
}
return computer;
}
}
}
// 使用 -- 流式 API
Computer pc = Computer.builder()
.cpu("Intel i7")
.memory("32GB")
.disk("1TB SSD")
.gpu("RTX 4070")
.os("Windows 11")
.build();何时使用:构造函数参数过多(>4 个)、参数可选、需要不可变对象、需要流式 API。Lombok 的 @Builder 注解可自动生成。
三、结构型模式
3.1 适配器模式 (Adapter)
意图:将一个接口转换为客户端期望的另一个接口,使原本不兼容的类可以一起工作。
java
// 已存在的旧接口(无法修改)
interface OldLogger {
void logMessage(String msg);
}
class OldLoggerImpl implements OldLogger {
@Override
public void logMessage(String msg) {
System.out.println("OLD: " + msg);
}
}
// 新的目标接口
interface NewLogger {
void info(String message);
void error(String message, Throwable t);
}
// 适配器 -- 将旧接口适配为新接口
class LoggerAdapter implements NewLogger {
private final OldLogger oldLogger;
public LoggerAdapter(OldLogger oldLogger) {
this.oldLogger = oldLogger;
}
@Override
public void info(String message) {
oldLogger.logMessage("[INFO] " + message);
}
@Override
public void error(String message, Throwable t) {
oldLogger.logMessage("[ERROR] " + message + ": " + t.getMessage());
}
}何时使用:整合遗留系统、对接第三方库、统一多个不同接口。Spring 的 HandlerAdapter 就是适配器模式。
3.2 装饰器模式 (Decorator)
意图:动态地给对象添加额外的职责,比继承更灵活。
java
// Java IO 就是装饰器模式的经典应用
// 组件接口
interface DataSource {
String readData();
}
// 具体组件
class FileDataSource implements DataSource {
private String data;
public FileDataSource(String data) { this.data = data; }
@Override
public String readData() { return data; }
}
// 装饰器基类
abstract class DataSourceDecorator implements DataSource {
protected final DataSource wrappee;
public DataSourceDecorator(DataSource source) { this.wrappee = source; }
}
// 加密装饰器
class EncryptionDecorator extends DataSourceDecorator {
public EncryptionDecorator(DataSource source) { super(source); }
@Override
public String readData() {
return decrypt(wrappee.readData());
}
private String decrypt(String data) {
return "解密后的: " + data;
}
}
// 压缩装饰器
class CompressionDecorator extends DataSourceDecorator {
public CompressionDecorator(DataSource source) { super(source); }
@Override
public String readData() {
return decompress(wrappee.readData());
}
private String decompress(String data) {
return "解压后的: " + data;
}
}
// 使用 -- 多层装饰
DataSource source = new CompressionDecorator(
new EncryptionDecorator(
new FileDataSource("原始数据")
)
);
source.readData(); // 解压后的: 解密后的: 原始数据Java IO 中的装饰器模式:
java
// FileInputStream 是具体组件,BufferedInputStream 是装饰器
InputStream in = new BufferedInputStream(
new DataInputStream(
new FileInputStream("data.bin")
)
);3.3 代理模式 (Proxy)
意图:为另一个对象提供替身或占位符以控制对它的访问。
java
// 接口
interface UserService {
User findById(Long id);
void save(User user);
}
// 真实实现
class UserServiceImpl implements UserService {
@Override
public User findById(Long id) {
// 模拟数据库查询
return new User(id, "user" + id);
}
@Override
public void save(User user) {
System.out.println("保存用户: " + user);
}
}
// 代理 -- 添加缓存
class CachingUserServiceProxy implements UserService {
private final UserService target;
private final Map<Long, User> cache = new ConcurrentHashMap<>();
public CachingUserServiceProxy(UserService target) {
this.target = target;
}
@Override
public User findById(Long id) {
return cache.computeIfAbsent(id, target::findById);
}
@Override
public void save(User user) {
cache.put(user.getId(), user);
target.save(user);
}
}
// 使用
UserService service = new CachingUserServiceProxy(new UserServiceImpl());Spring AOP 中的代理:Spring 使用 JDK 动态代理(接口代理)和 CGLIB 代理(类代理)实现 AOP 功能,如事务管理、日志切面等。
四、行为型模式
4.1 策略模式 (Strategy)
意图:定义一系列算法,把它们封装起来,使它们可以互相替换。
java
// 策略接口
interface PaymentStrategy {
void pay(BigDecimal amount);
}
// 具体策略
class AlipayStrategy implements PaymentStrategy {
@Override
public void pay(BigDecimal amount) {
System.out.println("支付宝支付: " + amount);
}
}
class WechatPayStrategy implements PaymentStrategy {
@Override
public void pay(BigDecimal amount) {
System.out.println("微信支付: " + amount);
}
}
class CreditCardStrategy implements PaymentStrategy {
@Override
public void pay(BigDecimal amount) {
System.out.println("信用卡支付: " + amount);
}
}
// 上下文 -- 使用策略
class PaymentContext {
private PaymentStrategy strategy;
public void setStrategy(PaymentStrategy strategy) {
this.strategy = strategy;
}
public void executePayment(BigDecimal amount) {
strategy.pay(amount);
}
}
// 使用 -- 替代 if-else
PaymentContext context = new PaymentContext();
context.setStrategy(new AlipayStrategy());
context.executePayment(new BigDecimal("100.00"));策略模式 + 枚举优化:
java
enum PaymentType {
ALIPAY(new AlipayStrategy()),
WECHAT(new WechatPayStrategy()),
CREDIT_CARD(new CreditCardStrategy());
private final PaymentStrategy strategy;
PaymentType(PaymentStrategy strategy) {
this.strategy = strategy;
}
public void pay(BigDecimal amount) {
strategy.pay(amount);
}
}
// 使用 -- 一行代码替代 if-else
PaymentType.valueOf(paymentMethod).pay(amount);4.2 观察者模式 (Observer)
意图:定义对象间的一对多依赖,当一个对象状态改变时,所有依赖者自动收到通知。
java
// 观察者接口
interface Observer {
void update(String event);
}
// 具体观察者
class EmailListener implements Observer {
@Override
public void update(String event) {
System.out.println("发送邮件: " + event);
}
}
class SmsListener implements Observer {
@Override
public void update(String event) {
System.out.println("发送短信: " + event);
}
}
// 被观察者(主题)
class OrderSubject {
private final List<Observer> observers = new ArrayList<>();
public void register(Observer observer) {
observers.add(observer);
}
public void unregister(Observer observer) {
observers.remove(observer);
}
public void notifyObservers(String event) {
for (Observer observer : observers) {
observer.update(event);
}
}
public void createOrder() {
// 创建订单逻辑
notifyObservers("订单已创建");
}
}Spring 事件机制:ApplicationEvent + @EventListener 就是观察者模式的实现。
4.3 模板方法模式 (Template Method)
意图:定义算法骨架,将某些步骤延迟到子类实现。
java
// 抽象类 -- 定义算法骨架
abstract class DataProcessor {
// 模板方法 -- final 防止子类修改
public final void process() {
loadData();
validateData();
transformData();
saveData();
afterProcess(); // 钩子方法
}
protected abstract void loadData();
protected void validateData() {
// 默认实现,子类可以覆盖
System.out.println("默认校验...");
}
protected abstract void transformData();
protected abstract void saveData();
// 钩子方法 -- 子类可选覆盖
protected void afterProcess() {
// 默认什么都不做
}
}
// 具体实现
class CsvProcessor extends DataProcessor {
@Override
protected void loadData() {
System.out.println("加载 CSV 文件");
}
@Override
protected void transformData() {
System.out.println("转换 CSV 数据");
}
@Override
protected void saveData() {
System.out.println("保存到数据库");
}
@Override
protected void afterProcess() {
System.out.println("发送处理完成通知");
}
}框架中的应用:Spring 的 AbstractApplicationContext(refresh 方法)、JdbcTemplate、MyBatis 的 BaseExecutor。
4.4 责任链模式 (Chain of Responsibility)
意图:将请求沿着处理链传递,直到被某个处理器处理。
java
// 处理器接口
abstract class Handler {
protected Handler next;
public Handler setNext(Handler next) {
this.next = next;
return next;
}
public void handle(Request request) {
if (doHandle(request)) {
return;
}
if (next != null) {
next.handle(request);
}
}
protected abstract boolean doHandle(Request request);
}
// 具体处理器
class AuthenticationHandler extends Handler {
@Override
protected boolean doHandle(Request request) {
if (!request.isAuthenticated()) {
System.out.println("认证失败");
return true; // 结束链
}
System.out.println("认证通过");
return false; // 继续传递
}
}
class RateLimitHandler extends Handler {
@Override
protected boolean doHandle(Request request) {
if (exceedLimit(request)) {
System.out.println("限流拒绝");
return true;
}
System.out.println("限流通过");
return false;
}
private boolean exceedLimit(Request request) { return false; }
}
class BusinessHandler extends Handler {
@Override
protected boolean doHandle(Request request) {
System.out.println("执行业务逻辑");
return true;
}
}
// 使用 -- 构建链
Handler chain = new AuthenticationHandler();
chain.setNext(new RateLimitHandler())
.setNext(new BusinessHandler());
chain.handle(request);框架中的应用:Servlet Filter 链、Spring Security 的过滤器链、Spring Interceptor 拦截器链、Netty 的 ChannelPipeline。
五、设计模式速查表
| 模式 | 分类 | 一句话描述 | 框架应用 |
|---|---|---|---|
| 单例 | 创建型 | 全局唯一实例 | Spring Bean 默认作用域 |
| 工厂方法 | 创建型 | 子类决定创建哪个对象 | Spring FactoryBean |
| 抽象工厂 | 创建型 | 创建产品族 | JDBC Connection |
| 建造者 | 创建型 | 分步构建复杂对象 | Lombok @Builder, StringBuilder |
| 原型 | 创建型 | 克隆对象 | Java Cloneable, BeanUtils.copy |
| 适配器 | 结构型 | 接口转换 | Spring HandlerAdapter |
| 装饰器 | 结构型 | 动态添加职责 | Java IO 流 |
| 代理 | 结构型 | 控制对象访问 | Spring AOP, MyBatis Mapper |
| 外观 | 结构型 | 提供统一接口 | SLF4J |
| 组合 | 结构型 | 树形结构 | 文件系统、菜单 |
| 享元 | 结构型 | 共享细粒度对象 | Integer 缓存, 连接池 |
| 桥接 | 结构型 | 分离抽象与实现 | JDBC Driver |
| 策略 | 行为型 | 算法族可替换 | Comparator, 支付方式 |
| 观察者 | 行为型 | 状态变化通知 | Spring Event, 消息队列 |
| 模板方法 | 行为型 | 定义算法骨架 | AbstractApplicationContext |
| 责任链 | 行为型 | 沿链传递请求 | Servlet Filter, Spring Security |
| 状态 | 行为型 | 状态驱动行为 | 订单状态机 |
| 命令 | 行为型 | 请求封装为对象 | Runnable, 线程池任务 |
| 迭代器 | 行为型 | 遍历集合 | Java Iterator |
| 中介者 | 行为型 | 协调对象交互 | MVC 中的 Controller |
| 备忘录 | 行为型 | 保存/恢复状态 | Undo 操作 |
| 访问者 | 行为型 | 分离算法与结构 | ASM 字节码操作 |
| 解释器 | 行为型 | 解析语法 | 正则表达式, SQL 解析 |
面试常见问题
Q1: 单例模式有几种实现方式?推荐哪种?
A: 四种主要方式:饿汉式、双重检查锁定(DCL)、静态内部类、枚举。推荐使用枚举单例,因为它最简单、线程安全、自动防止反射攻击和序列化破坏,是 Joshua Bloch 在《Effective Java》中推荐的实现方式。如果枚举不适用,静态内部类(Holder 模式)是第二选择。
Q2: 策略模式和工厂模式有什么区别?
A: 策略模式关注的是行为的选择,封装一系列可互换的算法,运行时动态切换。工厂模式关注的是对象的创建,封装实例化逻辑,返回合适的对象。策略模式解决的是"怎么做"的问题,工厂模式解决的是"创建哪个"的问题。两者常结合使用:工厂创建策略对象,策略执行具体行为。
Q3: 装饰器模式和代理模式的区别?
A: 装饰器模式是为了增强功能,装饰者和被装饰者实现同一接口,装饰者可以多层嵌套,关注于"在某功能上叠加更多功能"。代理模式是为了控制访问,代理控制对真实对象的访问,关注于"在访问真实对象前后做额外操作"(如 AOP 事务、日志)。从使用目的区分:装饰器是"加功能",代理是"控访问"。
Q4: 模板方法模式和策略模式的区别?
A: 模板方法使用继承,在父类中定义算法骨架,子类实现具体步骤,编译时确定行为。策略模式使用组合,定义策略接口,运行时动态切换算法。模板方法适合"整体流程固定,个别步骤可变"的场景;策略模式适合"算法族整体可替换"的场景。模板方法更强调流程控制,策略模式更强调算法替换。
Q5: 责任链模式在 Spring Security 中如何体现?
A: Spring Security 的过滤器链(SecurityFilterChain)是责任链模式的典型实现。每个过滤器(如 UsernamePasswordAuthenticationFilter、CsrfFilter)都负责一项安全检查,可以决定处理请求、修改请求或传递给下一个过滤器。这种设计使得安全策略可以灵活组合和配置,增删安全检查项无需修改核心代码。
