一、引言
在 Java 开发中,我们经常遇到这样的场景:根据不同的类型或条件,执行不同的业务逻辑。传统的写法往往是使用大量的 `if-else` 或 `switch-case`,随着业务增长,这种代码会变得难以维护。
今天,我要分享一套在实际项目中使用的高性能策略模式+工厂模式组合,能够让你的代码优雅百倍。
二、核心类设计
2.1 基础策略接口
/**
* <p>
* 基础策略接口 - 定义策略模式的标准实现
* </p>
* <p>
* 实现此接口的类可通过 {@link BaseStrategyFactory} 进行注册和管理,
* 支持按类型获取对应的策略实现。
* </p>
*
* @param <K> 策略标识类型(如枚举),用于唯一标识每个策略
* @param <Q> 请求参数类型,策略执行时需要的输入参数
* @param <R> 返回结果类型,策略执行后的输出结果
* @author Just.YangJie
* @version 1.0.1
* @since 2026-06-02
*/
public interface BaseStrategy<K, Q, R> extends InitializingBean {
/**
* 获取策略的唯一标识键
*
* @return K 策略标识,用于在工厂中注册和查找策略
*/
K getKey();
/**
* 执行策略逻辑
*
* @param request Q 策略执行所需的请求参数
* @return R 策略执行后的结果
*/
R execute(Q request);
}
2.2 基础工厂类
/**
* <p>
* 基础策略工厂 - 提供策略的注册与获取功能
* </p>
* <p>
* 使用全局注册表管理所有策略类型,支持根据类型和标识键获取对应的策略实现。
* 采用线程安全的 {@link ConcurrentHashMap} 存储,确保并发环境下的稳定性。
* </p>
*
* @param <K> 策略标识类型(如枚举)
* @param <S> 策略实现类型,必须实现 {@link BaseStrategy} 接口
* @author Just.YangJie
* @version 1.0.1
* @since 2026-06-02
*/
public abstract class BaseStrategyFactory<K, S extends BaseStrategy<K, ?, ?>> {
private static final Map<Class<?>, Map<Object, BaseStrategy<?, ?, ?>>> GLOBAL_REGISTRY = new ConcurrentHashMap<>();
/**
* 获取指定策略类型的注册表
*/
@SuppressWarnings("unchecked")
protected static <K, S extends BaseStrategy<K, ?, ?>> Map<K, S> registry(Class<S> type) {
return (Map<K, S>) GLOBAL_REGISTRY.computeIfAbsent(type, k -> new ConcurrentHashMap<>());
}
/**
* 注册策略实例到全局注册表
* <p>
* 如果相同类型和键已存在,则不会覆盖已有策略。
* </p>
*/
public static <K, S extends BaseStrategy<K, ?, ?>> void register(Class<S> type, K key, S strategy) {
Map<K, S> map = registry(type);
map.putIfAbsent(key, strategy);
}
/**
* 根据类型和标识键获取策略实例
*/
public static <K, S extends BaseStrategy<K, ?, ?>> S get(Class<S> type, K key) {
Map<K, S> map = registry(type);
return map.get(key);
}
/**
* 根据类型和标识键获取策略实例,若不存在则返回默认值
*/
public static <K, S extends BaseStrategy<K, ?, ?>> S getOrDefault(Class<S> type, K key, S defaultStrategy) {
Map<K, S> map = registry(type);
return map.getOrDefault(key, defaultStrategy);
}
}
三、设计亮点
3.1 三大核心优势
| 特性 | 说明 |
|---|---|
| 自动注册 | 实现 `InitializingBean`,在 Bean 初始化时自动注册到工厂 |
| 线程安全 | 使用 `ConcurrentHashMap` 作为全局注册表 |
| 类型安全 | 通过泛型确保策略与标识的类型一致 |
3.2 类图结构

四、实战案例:文件读取策略
4.1 业务场景
我们需要一个文件读取模块,支持 PDF、Word 等多种格式的内容提取。
4.2 定义标识枚举
/**
* 文件后缀 - 枚举
*/
@Getter
public enum FileExtensionEnum {
PDF("pdf", FileType.DOCUMENT),
DOC("doc", FileType.DOCUMENT),
DOCX("docx", FileType.DOCUMENT),
// ... 其他格式
;
private final String extension;
private final FileType fileType;
FileExtensionEnum(String extension, FileType fileType) {
this.extension = extension;
this.fileType = fileType;
}
}
4.3 定义请求响应对象
// 请求对象
@Data
@Builder
public class FileReadRequest {
private String path; // 文件路径
private FileExtensionEnum extension; // 文件扩展名
}
// 响应对象
@Data
@Builder
public class FileReadResponse {
private String content; // 文件内容
}
4.4 扩展策略接口
/**
* 文档读取策略接口
*/
public interface FileReaderStrategy
extends BaseStrategy<FileExtensionEnum, FileReadRequest, FileReadResponse> {
/**
* 策略注册方法 - 自动注册到工厂
*/
default void afterPropertiesSet(FileExtensionEnum key) {
FileReaderStrategyFactory.register(key, this);
}
/**
* 读取文件内容 (自定义方法名)
*/
FileReadResponse reader(FileReadRequest request);
@Override
default FileReadResponse execute(FileReadRequest request) {
return reader(request);
}
}
4.5 具体策略实现
PDF 读取策略:
@Slf4j
@Component
public class PdfReaderStrategy implements FileReaderStrategy {
@Override
public FileExtensionEnum getKey() {
return FileExtensionEnum.PDF;
}
@Override
public void afterPropertiesSet() {
// 自动注册到工厂
afterPropertiesSet(getKey());
}
@Override
public FileReadResponse reader(FileReadRequest request) {
try (PDDocument document = PDDocument.load(new File(request.getPath()))) {
PDFTextStripper stripper = new PDFTextStripper();
String content = stripper.getText(document);
return FileReadResponse.builder().content(content).build();
} catch (Exception e) {
log.error(e.getMessage(), e);
throw new CustomException(e.getMessage());
}
}
}
**Word 读取策略:**
@Slf4j
@Component
public class WordReaderStrategy implements FileReaderStrategy {
@Override
public FileExtensionEnum getKey() {
return FileExtensionEnum.DOCX;
}
@Override
public void afterPropertiesSet() {
// Word 支持 .doc 和 .docx 两种格式
afterPropertiesSet(getKey());
afterPropertiesSet(FileExtensionEnum.DOC);
}
@Override
public FileReadResponse reader(FileReadRequest request) {
// 根据不同格式处理
if (FileExtensionEnum.DOC == request.getExtension()) {
return readDocFormat(request);
}
return readDocxFormat(request);
}
private FileReadResponse readDocxFormat(FileReadRequest request) {
StringBuilder builder = new StringBuilder();
try (FileInputStream inputStream = new FileInputStream(request.getPath());
XWPFDocument document = new XWPFDocument(inputStream)) {
// 读取段落
for (XWPFParagraph paragraph : document.getParagraphs()) {
builder.append(paragraph.getText());
}
// 读取表格
for (XWPFTable table : document.getTables()) {
table.getRows().forEach(row ->
row.getTableCells().forEach(cell -> builder.append(cell.getText())));
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return FileReadResponse.builder().content(builder.toString()).build();
}
}
4.6 业务工厂类
/**
* 文档内容读取 工厂
*/
public class FileReaderStrategyFactory extends BaseStrategyFactory<FileExtensionEnum, FileReaderStrategy> {
/**
* 获取策略实例
*/
public static FileReaderStrategy getStrategy(FileExtensionEnum key) {
return BaseStrategyFactory.get(FileReaderStrategy.class, key);
}
/**
* 注册策略
*/
public static void register(FileExtensionEnum key, FileReaderStrategy strategy) {
BaseStrategyFactory.register(FileReaderStrategy.class, key, strategy);
}
}
4.7 调用示例
@Service
public class FileService {
/**
* 读取文件内容
*/
public String readFile(String path, FileExtensionEnum extension) {
// 1. 根据文件类型获取对应策略
FileReaderStrategy strategy = FileReaderStrategyFactory.getStrategy(extension);
// 2. 构建请求
FileReadRequest request = FileReadRequest.builder()
.path(path)
.extension(extension)
.build();
// 3. 执行策略获取结果
FileReadResponse response = strategy.execute(request);
return response.getContent();
}
}
五、注册原理详解
5.1 InitializingBean 自动注册机制
Spring 容器在 Bean 初始化完成后,会调用 `afterPropertiesSet()` 方法。我们利用这个钩子实现了**自动注册**:
```
Bean 创建 → afterPropertiesSet() 被调用 → 策略注册到工厂
```
5.2 全局注册表结构
GLOBAL_REGISTRY: Map<Class<?>, Map<Object, BaseStrategy<?, ?, ?>>>
第一层 Key: 策略实现类的 Class 对象 (如 FileReaderStrategy.class)
第一层 Value: 该类型下所有策略的 Map
第二层 Key: 策略的唯一标识 (如 FileExtensionEnum.PDF)
第二层 Value: 策略实例对象
**访问流程:**
-
调用 `FileReaderStrategyFactory.getStrategy(FileExtensionEnum.PDF)`
-
定位到 `FileReaderStrategy.class` 注册表
-
根据 `PDF` 键找到 `PdfReaderStrategy` 实例
六、更多应用场景
| 场景 | 策略标识 | 具体策略 |
|---|---|---|
| 支付 | PayTypeEnum | AlipayStrategy, WechatPayStrategy, UnionPayStrategy |
| 压缩 | CompressTypeEnum | ZipStrategy, RarStrategy, TarStrategy |
| 导出 | ExportTypeEnum | PdfExportStrategy, ExcelExportStrategy, WordExportStrategy |
| 短信 | SmsChannelEnum | AliSmsStrategy, TencentSmsStrategy, HuaweiSmsStrategy |
| 存储 | StorageTypeEnum | OssStorageStrategy, S3StorageStrategy, LocalStorageStrategy |
七、总结
通过策略模式+工厂模式的组合,我们实现了:
-
**代码解耦**:每个策略独立实现,互不依赖
-
**扩展性强**:新增策略只需实现接口,无需修改现有代码
-
**性能优异**:全局注册表 + ConcurrentHashMap,O(1) 查找
-
**自动注册**:利用 Spring 生命周期,零配置实现策略注册
-
**类型安全**:泛型确保编译期检查,避免运行时错误