一、引言

在 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: 策略实例对象


 

**访问流程:**
1. 调用 `FileReaderStrategyFactory.getStrategy(FileExtensionEnum.PDF)`
2. 定位到 `FileReaderStrategy.class` 注册表
3. 根据 `PDF` 键找到 `PdfReaderStrategy` 实例

六、更多应用场景

场景策略标识具体策略
支付PayTypeEnumAlipayStrategy, WechatPayStrategy, UnionPayStrategy
压缩CompressTypeEnumZipStrategy, RarStrategy, TarStrategy
导出ExportTypeEnumPdfExportStrategy, ExcelExportStrategy, WordExportStrategy
短信SmsChannelEnumAliSmsStrategy, TencentSmsStrategy, HuaweiSmsStrategy
存储StorageTypeEnumOssStorageStrategy, S3StorageStrategy, LocalStorageStrategy

七、总结

通过策略模式+工厂模式的组合,我们实现了:

1. **代码解耦**:每个策略独立实现,互不依赖
2. **扩展性强**:新增策略只需实现接口,无需修改现有代码
3. **性能优异**:全局注册表 + ConcurrentHashMap,O(1) 查找
4. **自动注册**:利用 Spring 生命周期,零配置实现策略注册
5. **类型安全**:泛型确保编译期检查,避免运行时错误

更多推荐