SpringBoot中使用策略模式优雅干掉if-else

/ 学习 / 没有评论 / 534浏览

前言

在阅读了一篇微信公众号文章《用设计模式干掉 if-else,太优雅了!》后,考虑到在SpringBoot项目中真实使用时,策略是IoC管理的而不是每次都由工厂创建,所以按自己的思路实现了一版比较简单的方案。

代码

代码清单

TypeEnum.java // 类型枚举类
ReceiptReq.java // 接口请求入参
ReceiptHandleStrategy.java // 接口
DefaultReceiptHandleStrategy.java // 默认接口实现
MT1101ReceiptHandleStrategy.java // MT1101类型对应的接口实现
Mt2101ReceiptHandleStrategy.java // MT2101类型对应的接口实现
ReceiptStrategyTest.java // 测试类

枚举类TypeEnum.java

package cn.cliveyuan.learn.test.strategy;

/**
 * @author Clive Yuan
 * @date 2023-10-20 16:32
 */
public enum TypeEnum {
    MT1101, MT2101, MT4101, MT8104, MT8105, MT9999
}

接口请求入参类ReceiptReq.java

package cn.cliveyuan.learn.test.strategy;

import lombok.Data;

/**
 * @author Clive Yuan
 * @date 2023-10-20 16:32
 */
@Data
public class ReceiptReq {
    private TypeEnum typeEnum;
    private String data;
}

接口类ReceiptHandleStrategy.java

package cn.cliveyuan.learn.test.strategy;

/**
 * @author Clive Yuan
 * @date 2023-10-20 16:33
 */
public interface ReceiptHandleStrategy {

    /**
     * 获取类型枚举 (每个类型实现类中指定)
     */
    TypeEnum getType();

    /**
     * 处理业务逻辑
     */
    void handleReceipt(ReceiptReq receipt);
}

默认接口实现类DefaultReceiptHandleStrategy.java

package cn.cliveyuan.learn.test.strategy;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @author Clive Yuan
 * @date 2023-10-20 16:34
 */
@Component
@Primary // 默认实现
public class DefaultReceiptHandleStrategy implements ReceiptHandleStrategy {
    /**
     * 从IoC容器中获取所有实现了ReceiptHandleStrategy接口的Bean
     */
    @Resource
    private List<ReceiptHandleStrategy> receiptHandleStrategyList;
    /**
     * 定义一个静态Map用于存储类型枚举对应的实现
     */
    private static final Map<TypeEnum, ReceiptHandleStrategy> STRATEGY_MAP = new HashMap<>();

    /**
     * 初始化
     * 遍历所有接口实现类,获取其类型并存入Map中
     */
    @PostConstruct
    public void init() {
        for (ReceiptHandleStrategy receiptHandleStrategy : receiptHandleStrategyList) {
            // 跳过type为空的实现类,即当前默认实现类DefaultReceiptHandleStrategy
            if (Objects.isNull(receiptHandleStrategy.getType())) continue;
            STRATEGY_MAP.put(receiptHandleStrategy.getType(), receiptHandleStrategy);
        }
    }

    @Override
    public TypeEnum getType() {
        // 当前为默认实现,这个类型不必填写
        return null;
    }

    @Override
    public void handleReceipt(ReceiptReq receipt) {
        // 从Map中获取对应类型的实现类
        ReceiptHandleStrategy receiptHandleStrategy = STRATEGY_MAP.get(receipt.getTypeEnum());
        // 判断是否为空
        if (Objects.isNull(receiptHandleStrategy)) {
            throw new RuntimeException("Not support this type:" + receipt.getTypeEnum());
        }
        // 调用对应接口
        receiptHandleStrategy.handleReceipt(receipt);
    }
}

MT1101类型对应的接口实现类MT1101ReceiptHandleStrategy.java

package cn.cliveyuan.learn.test.strategy;

import org.springframework.stereotype.Component;

/**
 * @author Clive Yuan
 * @date 2023-10-20 16:34
 */
@Component
public class MT1101ReceiptHandleStrategy implements ReceiptHandleStrategy {
    @Override
    public TypeEnum getType() {
        // 填写对应的类型枚举
        return TypeEnum.MT1101;
    }

    @Override
    public void handleReceipt(ReceiptReq receipt) {
        // 处理对应的业务场景
        System.out.println("解析报文MT1101:" + receipt.getData());
    }
}

MT2101类型对应的接口实现类Mt2101ReceiptHandleStrategy.java

package cn.cliveyuan.learn.test.strategy;

import org.springframework.stereotype.Component;

/**
 * @author Clive Yuan
 * @date 2023-10-20 16:34
 */
@Component
public class Mt2101ReceiptHandleStrategy implements ReceiptHandleStrategy {
    @Override
    public TypeEnum getType() {
        // 填写对应的类型枚举
        return TypeEnum.MT2101;
    }

    @Override
    public void handleReceipt(ReceiptReq receipt) {
        // 处理对应的业务场景
        System.out.println("解析报文MT2101:" + receipt.getData());
    }
}

测试类ReceiptStrategyTest.java

package cn.cliveyuan.learn.test.strategy;

import cn.cliveyuan.learn.test.BaseTest;
import org.junit.Test;

import javax.annotation.Resource;

/**
 * @author Clive Yuan
 * @date 2023-10-20 16:36
 */
public class ReceiptStrategyTest extends BaseTest {
    @Resource
    private ReceiptHandleStrategy receiptHandleStrategy;

    @Test
    public void test_01() {
        ReceiptReq receipt = new ReceiptReq();
        receipt.setTypeEnum(TypeEnum.MT1101);
        receipt.setData("Hi, MT1101");
        receiptHandleStrategy.handleReceipt(receipt);
    }
    @Test
    public void test_02() {
        ReceiptReq receipt = new ReceiptReq();
        receipt.setTypeEnum(TypeEnum.MT2101);
        receipt.setData("Hi, MT2101");
        receiptHandleStrategy.handleReceipt(receipt);
    }
    @Test
    public void test_03() {
        ReceiptReq receipt = new ReceiptReq();
        receipt.setTypeEnum(TypeEnum.MT4101);
        receipt.setData("Hi, MT4101");
        receiptHandleStrategy.handleReceipt(receipt);
    }
}

基础测试类BaseTest.java

package cn.cliveyuan.learn.test;


/**
 * 基本测试
 * @author Clive Yuan
 * @date 2023-10-20 16:36
 */

import cn.cliveyuan.learn.Application;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class BaseTest {
}

测试结果

test_01: 解析报文MT1101:Hi, MT1101
test_02: 解析报文MT2101:Hi, MT2101
test_03: java.lang.RuntimeException: Not support this type:MT4101

扩展性

如果要新加一个处理类型,只需2步:

  1. 在类型枚举TypeEnum.java中新增枚举值
  2. 新增一个接口ReceiptHandleStrategy的实现,并填写对应的类型枚举

结语

ReceiptHandleStrategy接口调用时只需要依赖这个接口即可,因为默认实现加了@Primary注解就会默认调用这个实现处理,并根据类型枚举路由到对应的实现,在实现的类里面就可以方便地依赖其他Bean做业务处理了。 你对这种方式有什么看法呢? 评论区见^_^

@Author: Clive 博客地址:cliveyuan.cn