设计模式的优雅:函数式pipeline+策略模式实现复杂业务@落雨

现在有一个新需求,要求对老接口进行升级,原有逻辑基础上做功能路由,识别老业务走老接口,命中新业务(灰度)则走新接口,且新老接口出入参焕然一新,完全不同,但是要保证原有接口出入参一致(相当于强行换*还不要影响线上业务,前端都无需改动)。你会怎么设计?本篇文章提供2种方式来解决

流程图:

设计模式的优雅:函数式pipeline+策略模式实现复杂业务@落雨

一、常规做法(最简单的玩法)

增加路由处理类,分别路由到原有接口和新接口,由各自接口进行处理

if(老逻辑){
  doSomeThingOld(context);
} else {
  doSomeThingNew(context);    
}

二、优雅做法(抽象、piple、策略)

如果按照常规模式,那么这个需求就结束了,只需在入口层增加一个路由,后面各自的接口自己去实现入参校验、参数组装、逻辑处理、结果变换、log打印,但是你稍加思索就觉得,哎呀,这一套组合拳下来,岂不是一样的流程,很好,我们开始抽象。

2.1 抽出函数处理器,负责执行函数

/**
* Pipe执行器,使用Stream.map处理各层Fuction
*  <pre>
     Function<ItemResultContext, ItemResultContext>> 的含义是使用一个ItemResultContext入参,传出一个ItemResultContext出参
   </pre>
* @param context
* @param functionList
* @return
*/
private Optional<ItemResultContext> buildItemResult(ItemResultContext context,
   List<Function<ItemResultContext, ItemResultContext>> functionList) {
   // 获取context流,context是每个函数的出入参
   Stream<ItemResultContext> stream = Stream.of(context);
   if (CollectionUtils.isEmpty(functionList)) {
       return Optional.empty();
   }

   // 同步执行,循环处理函数流, stream1 -> stream2 -> stream3 -> ... streamN
   for (Function f : functionList) {
       // @link{<R> Stream<R> map(Function<? super T, ? extends R> mapper);}
       stream = stream.map(f);
   }

   return stream.findFirst();
}

2.2 抽出pipeline函数处理器,用来定义流程

/**
* Pipe组装
* @param context
* @param functionList
* @return
*/
public ItemResultContext buildItemResult(ItemResultContext context) {
        // 组装流程
        List<Function<ItemResultContext, ItemResultContext>> list = Lists.newArrayList();
        list.add(logAroundBuildItemResult("covertReq", this::covertReq));
        list.add(logAroundBuildItemResult("queryItems", this::queryItems));
        list.add(logAroundBuildItemResult("covertItems", this::covertItems));
        list.add(logAroundBuildItemResult("queryCategorys", this::queryCategorys));
        list.add(logAroundBuildItemResult("combineResult", this::combineResult));
        Optional<ItemResultContext> opt = buildItemResult(context, list);
        return opt.orElse(context);
    }

2.3 抽出log处理,环绕切面,切函数

/**
* Pipe环绕切面,apply -> function
* @param desc
* @param func
* @return
*/
private Function<ItemResultContext, ItemResultContext> logAroundBuildItemResult(String desc,
   Function<ItemResultContext, ItemResultContext> func) {
   return req -> {
       long start = System.currentTimeMillis();
       log.error("[{}] start", desc);
       ItemResultContext resp = func.apply(req);
       log.error("[{}] finish, time elapsed {}ms", desc, System.currentTimeMillis() - start);
       return resp;
   };
}

2.4 将此函数处理器、切面环绕等基础方法抽到父类,提取抽象方法,让子类来实现函数pipe组装,让子类来实现更多的业务场景

@Slf4j
public abstract class AbstractItemManager implements ItemManager {

    /**
     * 获取结果
     * @param context
     * @return
     */
    public abstract ItemResultContext buildItemResult(ItemResultContext context);

    /**
     * 转换req到上下文
     * @param context
     * @return
     */
    public abstract ItemResultContext covertReq(ItemResultContext context);

    /**
     * 查询商品
     * @param context
     * @return
     */
    public abstract ItemResultContext queryItems(ItemResultContext context);

    /**
     * 转换结果
     * @param context
     * @return
     */
    public abstract ItemResultContext covertItems(ItemResultContext context);

    /**
     * 查询类目
     * @param context
     * @return
     */
    public abstract ItemResultContext queryCategorys(ItemResultContext context);

/**
     * Pipe执行器
     * @param context
     * @param functionList
     * @return
     */
    private Optional<ItemResultContext> buildItemResult(ItemResultContext context,
        List<Function<ItemResultContext, ItemResultContext>> functionList) {
        // 获取context
        Stream<ItemResultContext> stream = Stream.of(context);
        if (CollectionUtils.isEmpty(functionList)) {
            return Optional.empty();
        }

        // 函数列表执行,同步执行
        for (Function f : functionList) {
            stream = stream.map(f);
        }

        return stream.findFirst();
    }

    /**
     * Pipe环绕切面,apply -> function
     * @param desc
     * @param func
     * @return
     */
    private Function<ItemResultContext, ItemResultContext> logAroundBuildItemResult(String desc,
        Function<ItemResultContext, ItemResultContext> func) {
        return req -> {
            long start = System.currentTimeMillis();
            log.error("[{}] start", desc);
            ItemResultContext resp = func.apply(req);
            log.error("[{}] finish, time elapsed {}ms", desc, System.currentTimeMillis() - start);
            return resp;
        };
    }
}

2.5 完事,很清爽

落雨 2021-09-10 20:12:45
http://www.js-dev.cn

设计模式的优雅:函数式pipeline+策略模式实现复杂业务@落雨

上一篇:2019日本Summer Sonic音乐节攻略(明星阵容)


下一篇:浅析什么是数据库主从复制、主从复制作用及其实现原理;什么是数据库读写分离架构、读写分离带来的好处及局限性、为什么使用缓存代替读写分离;什么是数据库水平切分架构及其解决的问题