利用 JDK8 函数式编程,让你的代码更加优雅

场景

没有业务场景的示例都是在耍流氓

今天领导让做下 企业微信 的对接工作(应该不少人都有在做)

之前就写过一两个接口,就简单写了下

逻辑

以获取群聊信息为例:

  1. 根据 corpidsecret(应用密钥) 获取 access_token,即获取应用的授权,不同应用 secret 不同
  2. access_token 的获取是不能频繁去获取的,否则可能会被 IP 限制,因此我们把 token 保存到 Redis 中,每次先从 Redis 中获取,没有或者 token 过期 再去提交请求获取 access_token
  3. 调用获取群聊信息的接口
  4. 判断企业微信的返回值 errcode,是否有密钥过期
  5. 如果密钥过期,获取 access_token 再次 调用获取群聊信息的接口

反面教材

所有接口都是这种套娃式的逻辑,只有一个字形容

@Resource
private AppchatService appchatService;

/**
 * 获取群聊
 * @param chatid
 * @return
 */
@GetMapping(value = "/get/{chatid}")
public ErrorMsg get(@PathVariable("chatid") String chatid) {
    // 获取 token
    String accessToken = tokenService.getToken(wechatInfo.getAppchatSecret());
    ErrorMsg msg = appchatService.get(accessToken, chatid);
    // 如果发生错误,可能是密钥过期,尝试更新密钥再获取
    if (msg.getErrorCode().equals(ErrorMsg.ACCESS_LIMITED.getErrorCode())) {
        accessToken = tokenService.updateToken(wechatInfo.getAppchatSecret());
        msg = appchatService.get(accessToken, chatid);
    }
    return msg;
}

get 方法的实现

@Override
public ErrorMsg get(String accessToken, String chatid) {
    if (StringUtils.isBlank(chatid)) {
        return ErrorMsg.ARGS_ERROR.setNewErrorMsg("群聊 id 不能为空");
    }
    String url = wechatInfo.getIp() + "/cgi-bin/appchat/get?access_token=" + accessToken + "&chatid=" + chatid;
    String json = RestTemplateUtil.get(url, null);
    AppchatResult result = JsonUtil.toBean(json, AppchatResult.class);
    if (!"0".equals(result.getErrcode())) {
        return ErrorMsg.ACCESS_LIMITED.setNewErrorMsg(result.getErrmsg());
    }
    return ErrorMsg.SUCCESS.setNewData(result);
}

其他例子
在这里插入图片描述

函数式编程优化代码

定义函数式接口

这个接口就是我们被代理的函数的抽象,其中自定义的 apply 方法就是去执行方法

@FunctionalInterface: 用于定义接口为函数式接口,接口下有且仅有一个抽象方法

参数: 可以看到 apply 方法中后面的参数只有一个,就是 accessToken 因为 只有这个参数,我们需要动态修改

/**
 * 函数式,接口执行
 */
@FunctionalInterface
public interface ApplyFunction {
    ErrorMsg apply(String accessToken);
}

对前文中的逻辑进行封装

/**
 * 执行操作
 * @author: linjinp
 * @create: 2020-06-18 11:19
 **/
@Component
public class OperatorFunction {

    @Resource
    private TokenService tokenService;

    public ErrorMsg doOperate(String secret, ApplyFunction function) {
        // 获取 token
        String accessToken = tokenService.getToken(secret);
        // 执行代理的函数
        ErrorMsg msg = function.apply(accessToken);
        // 如果发生错误,可能是密钥过期,尝试更新密钥再获取
        if (msg.getErrorCode().equals(ErrorMsg.ACCESS_LIMITED.getErrorCode())) {
            accessToken = tokenService.updateToken(secret);
            msg = function.apply(accessToken);
        }
        return msg;
    }
}

函数式接口调用

accessToken: 这个参数需要变动,因此这里的 val 即前文中函数式接口的参数

chatid: 这个参数是固定的,不需要变动,因此直接传就行了,不需要通过函数式接口

/**
 * 获取群聊
 * @param chatid
 * @return
 */
@GetMapping(value = "/get/{chatid}")
public ErrorMsg get(@PathVariable("chatid") String chatid) {
    String secret = wechatInfo.getAppchatSecret();
    return operatorFunction.doOperate(secret, (val) -> appchatService.get(val, chatid));
}

调用示例

开始调用函数式接口

以读取用户信息接口为例,这里 secretnull 情况下,getToken 会给 secret 一个默认值,因此不需要管
在这里插入图片描述
请求成功,如果不成功,就会更新 token 再请求一次
在这里插入图片描述

优化后的代码

在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章