关于接口可维护性的一些建议

科技资讯 投稿 5400 0 评论

关于接口可维护性的一些建议

作者:京东科技 D瓜哥

    在接口注释中加入接口文档链接

  1. 将接口源代码发布到私服仓库

  2. 如果使用 Map 对象作为传输载体,要提供 Key 值定义常量

  3. 尽可能简化接口依赖

  4. 将接口的参数和返回值原始数据打印到日志中

  5. 核心思想:以人为本,就近原则,触手可及

1. 在接口注释中加入接口文档链接

在做接口开发时,无论是对自有接口的升级改造,还是针对外部接口的从头接入,都涉及到接口文档。不同之处是,前者的工作重点是书写或者更新接口文档;而后者是根据接口文档开发合适的接入代码。但是,经常遇到的一个麻烦是,找不到接口文档。在组内需要找老同事询问;如果是跨部门,还需要两层甚至三层的进行转接,非常麻烦。

2. 将调用接口处写上被调用接口文档链接

在调用其他系统的接口时,没有接口文档,几乎寸步难行。在第一次接入接口时,绝大多数情况下,都是参考着接口文档做接入工作。但是,目前的情况时,接入时参考文档,参考完就随手把文档给“扔了”。后续如果还需要做进一步升级维护,还需要到处找接口文档;另外,交互的系统难免有一些 Bug,在和其他系统维护人员对接处理 Bug 时,只有接口没有文档,对方可能也需要去找文档链接。无形中,很多时间都浪费在了找文档的过程中。

经过最近一段时间的实践情况来看,这个处理非常方便,是一个非常值得推广的实践。再插一句,也可以像一条建议一样,可以在维护代码时,不断把已接入的接口文档加入到调用接口的地方,循序渐进,方便后续人维护升级。

3. 将接口源代码发布到私服仓库

这里只说明一下 Java 的相关处理办法。如果使用 Maven 作为构建工具的话,默认是不会将源代码发布到私服仓库中的。关于如何将源代码发布到,在 升级 Maven 插件:将源码发布到私服仓库 中已经做过相关介绍,这里就不再赘述。

4. 对于状态值常量,优先在接口参数类或者返回值类中定义

在做接口开发时,很多数据都有一个状态值,比如订单状态,再比如接口状态等等。目前的一个情况时,这些状态值大部分书写在文档中,在接入接口时,需要接入方自定义这些状态值。这就有些繁琐了,而且状态定义也不明确,甚至有可能遗漏一些重要的状态值。有些懒省事,直接在代码中硬编码一个魔法值,后续维护的跟还需要根据上下文反推这个值的含义,非常不利于维护。

    如果状态值不是很多,优先在接口参数类或者返回值类中定义。

这样使用的时候,触手可及。不需要到处去找。

5. 如果使用 Map 对象作为传输载体,要提供 Key 值定义常量

Map 作为数据载体。自己开发的时候很爽,但是给接口接入却非常不友好。接入方从 Map 中获取数据时,要么自己定义 Key 值;要么直接使用魔法值硬编码在代码中。使用前者方案,就需要在各个接入方都需要自定义一套;使用后者,初期是省事了,后来维护的人员就懵逼了。这都无形中增加了很多维护成本。

6. 针对 Map 返回值,可以考虑使用将 Map 转化成对象

针对 Map 的处理,即使按照 如果使用 Map 对象作为传输载体,要提供 Key 值定义常量 推荐的做法,定义了相关的 Key,在取值时,也略有麻烦,需要不断的 map.get(KEY。一个更简单的方法是自定义一个类型,使用工具将 Map 对象转化成自定义类型的对象。这样就可以直接使用方法调用来取值。

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;

import java.util.*;java

/**
 * Map 工具类
 *
 * @author D瓜哥 · https://www.diguage.com
 */
@Slf4j
public class MapUtils {
    private static final ObjectMapper MAPPER = new ObjectMapper(;

    static {
        MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false;
    }

    /**
     * 将 Map 转换成指定类型的对象
     *
     * @author D瓜哥 · https://www.diguage.com
     */
    public static <T> T convertToObject(Map<String, Object> data, Class<T> clazz {
        try {
            T result = MAPPER.convertValue(data, MAPPER.getTypeFactory(.constructType(clazz;
            if (log.isInfoEnabled( {
                log.info("converted {} to a {} object: {}",
                        JsonUtils.toJson(data, clazz.getSimpleName(, JsonUtils.toJson(result;
            }
            return result;
        } catch (Exception e {
            log.error("converting failed! data: {}, class: {}",
                    JsonUtils.toJson(data, clazz.getSimpleName(, e;
        }
        return null;
    }

    /**
     * 将 Map 转换成指定类型的对象
     *
     * @author D瓜哥 · https://www.diguage.com
     */
    public static <T> List<T> convertToObjects(List<Map<String, Object>> datas, Class<T> clazz {
        if (CollectionUtils.isEmpty(datas || Objects.isNull(clazz {
            return Collections.emptyList(;
        }
        List<T> result = new ArrayList<>(datas.size(;
        if (CollectionUtils.isNotEmpty(datas {
            for (Map<String, Object> data : datas {
                T t = convertToObject(data, clazz;
                result.add(t;
            }
        }
        return result;
    }
}


7. 尽可能简化接口依赖

现在,很多对外暴露接口的定义是,接口定义放在一个模块中;模型定义在一个模块中;有些工具类又定义在一个模块中。接口依赖模型模块;模型模块又依赖工具类模块;而工具类依赖了一大堆外部依赖。个人觉得这是一个非常不好的实践。会导致很多不必要的依赖被间接引入到了接口使用方的系统中,无形中增加很多维护成本。

对于前面 对于状态值常量,优先在接口参数类或者返回值类中定义 中提到了“如果状态值很多,可以考虑单独抽取成一个常量类或者枚举类。” 这里存在一种情况需要特别说明,状态值的定义需要在本系统的业务模块的代码中使用,可以将接口的依赖加入到改业务模块的依赖中,而不是反过来。为什么会这样的操作?一个核心思想是保持对外暴露接口的纯净性。这样既可以减少状态定义的重复性,又可以减少接口的外部依赖。

8. 只传递必要字段,尽量避免大而全的接口

D瓜哥认为,在做接口开发时,一定要做一个“吝啬的守财奴”。把数据当做财富一样守护,对外只提供必要的数据,做到“够用就行”。

关于传输效率上的一些思考,结合 Hessian、Msgpack 和 JSON 实例对比 以及 “Hessian 协议解释与实战” 等文章来看,有几个原则值得重视的:

  1. boolean 型满足不了,次优选择 int 整型数据;再次可以考虑 long 型;

  2. 对于以上类型不满足,则选择使用字符串。

  3. 以上情况都不符合要求才选择自定义对象。

9. 将接口的参数和返回值原始数据打印到日志中

所以,一定要谨记,将接口的参数和返回值原始数据打印到日志中。D瓜哥凭借这个实践,在一些客诉及反馈中,顺利脱身,实现完美甩锅。

10. 将 RPC 接口的类名及方法打印到日志中

这里为什么和上面的 将接口的参数和返回值原始数据打印到日志中 单独列出来?因为,在这个实践中,强调的是 “RPC 接口”。相对来说,RPC 接口存在更多容易出错的问题,经常需要脱离系统去单独测试 RPC 接口的可用性。把类名就方法名可以更方便在出现问题时,就可以及时根据日志中的信息,去单独测试 RPC 的可用性。

11. 核心思想:以人为本,就近原则,触手可及

对于可维护性建议的一个核心思想就是:以人为本,就近原则,触手可及。通常来说,人都是有一定的惰性的。如果把饭端到眼前,相信任何正常人无法抗拒美食的诱惑。而这里提到的一些可维护性的点,就是尽可能照顾人“懒”的特性,在第一次时,就把该做的工作做到位,减少后续人员不必要的麻烦,让人可以“合法偷懒”。

加油!争取让更多人可以更好地偷懒。💪🏻💪🏻💪🏻

编程笔记 » 关于接口可维护性的一些建议

赞同 (29) or 分享 (0)
游客 发表我的评论   换个身份
取消评论

表情
(0)个小伙伴在吐槽