🌑

Shawn Fux

策略模式的实现与落地

最近在做一个电商项目,接到一个需求需要接入第三方应用的商品库,对方需要我方提供一个回调接口,会将诸如商品上下架订单发货等系统事件通过回调通知到我方,但是他所有的回调系统事件都是通过一个接口进行通知的,只是在参数通过一个枚举字段值标识当前的回调是一个什么系统事件

前提背景

下为第三方的回调参数,会有两个字段,一个是 type 标识当前的回调操作类型,另一个是 data 回调的参数,根据操作类型不同这个 JSON 字符串 也会不同,此为前提背景。

{
    "type":"PRODUCT_UP",
    "data":"{\"productId\":\"32362763723633627\"}"
}

由于回调的操作类型多达十几种,如果使用 if else 或者 switch 也勉强能实现,但是这样可维护性可读性都很差,后期扩展新的回调操作类型,需要频繁修改旧的代码,也不利于扩展,随想到使用策略模式来处理这种回调。

抽象接口

首先定义一个抽象接口,这是实现策略模式至关重要的第一步,因为不管是什么操作类型的回调,其归根结底我们是需要去处理这个操作的,而因为不同的操作类型其处理参数和处理过程都不同,所以处理这个操作我们可以定义为一个抽象接口:

@FunctionalInterface
public interface CallBackStrategy {
    
    void handle(String json);
}

CallBackStrategy 接口定义了一个 handle 方法,参数为一个 JSON 字符串,由于不同操作类型参数 data 所传递的 JSON 字符串格式会不一样,所以我们原样的将这个 JSON 字符串传递给实际的实现类去序列化处理它。

具体实现类

这里以商品上架回调这一个操作为例,实现类如下,实现 CallBackStrategy 接口的 handle 方法将参数 JSON 字符串序列化为对象,然后拿到 productId 去调用第三方接口获取完整的商品信息进行同步落库。

@Component("PRODUCT_UP")
public class ProductUpStrategy implements CallBackStrategy{

    @Override
    public void handle(String json) {
        ProductData productData = JSONUtil.toBean(json, ProductData.class);
        // ....解析得到商品ID,调用接口获取完整的商品信息同步
        // ....其它的一些操作
    }
}

策略注册

现在我们接口和实现类都定义好了,那么如何将它们组织在代码中运用起来了,这里我以 Spring 环境为例,假设回调的接口定义如下:

@RestController
@RequestMapping
public class CallBackController {

    @Resource
    private Map<String,CallBackStrategy> strategyMap;
    
    @PostMapping("/callBack")
    public String callBack(@RequestBody String body){
        Map map = JSONUtil.toBean(body, Map.class);
        String type = (String) map.get("type");
        CallBackStrategy strategy = strategyMap.get(type);
        strategy.handle(body);
        return "ok";
    }
}

CallBackController 类有一个属性 strategyMap ,这个 Map 集合保存了 CallBackStrategy 接口的实现类,键为这个策略实现类具体能够处理哪种 type 类型的值,得益于 Sring 的依赖注入,它会为我们找到所有 CallBackStrategy 接口的实现类,前提是这个实现类必须是一个 Bean 对象,如果你不是在 Sring 环境运行,那么你可能需要自己手动将实现类注册到 strategyMap 集合里面。

接着我们在 callBack 方法中,从传递的 JSON 字符串中解析 type 类型的值,然后以 type 为 key 去 strategyMap 中查找对应的策略实现类去处理这条回调消息。

— Jun 19, 2023