自己动手写 SDK 的经验技巧分享

大家好,我是鱼皮。

最近因为工作需要,自己动手写了一些项目的通用 SDK。在编写的过程中,我阅读和参考了不少公司中其他大佬写的 SDK,也总结了一些开发 SDK 的经验和技巧,给大家分享下~

在此之前,必须先给大家解释一下啥是 SDK。

啥是 SDK ?

SDK(Software Development Kit)即 软件开发工具包 ,就是帮助我们开发出软件的工具集合,除了代码之外,一般还要搭配文档、示例等。

一般 SDK 都是需要 引入 到项目中使用的。比如学 Java 的朋友最早接触的 JDK,就是用来开发 Java 软件的工具包,使用时需要编写 类似 import java.util.* 的语法来引入。此外,大部分的 SDK,都是需要通过人工或项目管理工具,将其文件下载到指定路径才能引入。

使用 SDK 有什么好处呢?

举个例子,假设公司有很多系统都需要实现文件上传功能。之前看过我文章的朋友应该知道,一个优秀的文件上传功能并不好做,要考虑很多点,比如分块、断点续传、秒传、文件存储、文件管理等。

文件上传设计:https://mp.weixin.qq.com/s/3QXe4MSObJTP43M2gXWSlA

显然,我们不需要给每个系统都去开发文件上传,而是只需要有一个团队舍身而出,编写一套 通用的 文件上传 SDK,然后让需要实现同样功能的系统引用就行了,这样就 大大减少了工作量、提高了开发效率

有点前人造车,后人享乐的意思~

编写 SDK 又称 造轮子 ,好的轮子不仅能够帮助团队省时省力,还能够减少一些项目在相同功能上的差异。就不要说同一个功能,小王写的要运行 1 秒,小李写的要运行 1 小时!

而假设每个系统都去开发同样的功能,那就是 重复造轮子 ,在大多数情况下,不是明智之举。

理解了啥是 SDK 后,来看看如何写出优秀的 SDK 吧~

手写 SDK 经验总结

好的 SDK 应该具有简单易用、通俗易懂、便于扩展、高效稳定等特点。

易用性

如今,现成的轮子实在太多了!如何让你的轮子脱颖而出呢?那就要先提升 SDK 的易用性。

我自己在技术选型时,就会倾向于优先选择简单易用的 SDK,最好是几行代码就能轻松使用,而不是必须要我读完老长一份文档,再写个几十行代码才能生效。

就和产品说明书一样,太复杂直接把人劝退。

我们可以通过以下几点提高易用性:

统一调用

将复杂的功能进行封装,对外提供统一的调用入口,尽量屏蔽一些实现细节,减少用户调用的流程和对参数的理解成本。

举个例子,下面是两种日期处理函数。用户不需要关心他们是如何实现的,只需要知道怎么用、传递哪些参数、得到哪些返回值就行了。

// 第 1 种:需要 new 对象
DateUtils dateUtils = new DateUtils();
dateUtils.setDate('2021-08-31');
Date date = dateUtils.parse(); // 第 2 种:直接调用
Date date = DateUtils.parse('2021-08-13');

那大家觉得哪种更易用呢?

集中配置

将复杂的参数配置化,不需要让用户到处写参数,而是通过一个配置文件统一管理。

Java 主流开发框架 SpringBoot 就是典型的例子,假如用户想改变内嵌服务器启动的端口、亦或是改变数据库的连接地址,不需要写代码,而是改一下配置文件就行了:

# 服务器配置
server:
port: 8081
# 数据库配置
db:
ip: 10.0.0.1

此外,这样也便于维护项目和实现多环境。

良好命名

给 SDK 的函数取名称时,尽量让它符合用户的习惯。

比如具有解析功能的函数,可以叫 "parseXXX";判断是否为空的函数,可以叫 "xx.isEmpty" 等。最好能做到让用户不看文档,只通过函数名称和参数,就知道你这个函数是做什么的。

因此,想要写出好的 SDK,首先要多用、多参考别人的 SDK,养成习惯后你就会发现,大家起名儿都差不多。

但也要注意一点,如果可以,尽量不要让你 SDK 中的类名(函数名)和别人的完全一致,否则可能给用户带来困扰:这么多同名的函数,我该用哪个呢?哪个是你开发的 SDK 呢?

可理解性

有时,用户可能不满足于简单地使用你的 SDK,而是希望阅读你的 SDK 源码来进一步了解,用的才更放心。

因此,除了易用外,还要让你的 SDK 便于理解,不能金玉其外败絮其中。

这个就和编码习惯有很大的关系了,无论是写 SDK 也好,还是做项目也罢,都要做到以下几点:

结构清晰

把代码按照功能或类别进行整理,放到指定的目录下。常见的做法有分包、分层等,让人一眼就知道每个目录下的文件的作用。

比如下面这个经典的 Java 项目结构,service 目录是编写业务逻辑的、constant 是存放常量的、utils 是存放工具类的等等,都很清晰:

统一风格

统一风格的目标很简单:让项目看起来是同一个人写出来的。

比如代码缩进都用 4 个空格、命名都用驼峰式等。尤其是功能相似的代码,一定要保持命名和用法的统一!比如解析文本的函数,不要一会叫 "parseXXX"、一会儿又叫 "jiexiXXX",给用户都整乐了~

但实际上,团队开发中,很难做到这点。因此才需要有一套通用的代码规范,大家都去遵守规范,才能让项目更好理解、更便于维护。

编写注释

最好给 SDK 中的每个类和函数的 开头 都加上注释,这样用户在使用 SDK 时,甚至都不需要看文档,直接看代码注释就能知道它是干嘛的、怎么用。

随便打开 Java SDK 或者网上知名 SDK 中的一个函数,一般都能看到这些注释,包括对函数功能的描述、参数含义、返回值含义等:

说明文档

除了注释外,还要编写一个说明文档(用户手册),包括如何引入 SDK 、有哪些功能、应该怎么使用等等,甚至还可以补充一些关键的实现细节、以及常见的问题列表。

这点也会极大地影响用户的选择。就我个人而言,没有文档的 SDK 我一般是不会选用的,万一出了事我找谁呢?

可扩展性

编写 SDK 的一大难点是:不仅要考虑到大部分通用的使用场景,还要满足小部分用户定制化的需求。

因此,SDK 的可扩展性是很重要的,但怎么提升呢?

轻量依赖

一方面,我们可以尽量减少 SDK 本身对其他类库的依赖。

举个例子,假如你要做一个很轻小的工具类,可能只有几十 KB,那就没有必要再引入一个几百 KB 的依赖库了,得不偿失!别人也不乐意用啊!

轻量依赖不仅可以减少 SDK 的体积,更关键的是可以减少依赖冲突的可能性。我自己也曾经遇到过很多次这样的尴尬局面:引入一个工具类后,整个项目就跑不起来了!

自定义实现

为了提高 SDK 的通用性和灵活性,在设计 SDK 时,除了提供默认实现外,建议提供一个通用接口或抽象类,让用户来继承,编写自己的实现方式。

举个例子,假设我们要编写一个日期解析类,默认的解析规则是按照短横分割字符串:

// 按照 '-' 切分
date = DateUtils.parse('2021-01-18')

如果只能做到这点,那这个 SDK 就很死板。因为用户可能想按照冒号或其他规则来解析。

怎么实现呢?

我们可以允许用户自己传入分割字符:

// 按照 '-' 切分
parseRule = ':'
date = DateUtils.parse('2021-01-18', parseRule)

还可以让用户自己来控制解析的方式:

// 自定义解析器
interface MyParser extends Parser {
// 需要用户自己实现
void doParse();
}
// 指定解析器
date = DateUtils.parse('2021-01-18', MyParser.class);

这两种方式在 SDK 的设计中屡见不鲜,此外还可以让用户自行编写或指定配置文件,也能提高灵活性。

高效稳定

其实,开发 SDK,尤其是在大厂开发 SDK,是个很 “坑” 的工作,我相信做过的朋友会感同身受。

因为随着使用你 SDK 的用户越来越多,可能会发现各种各样莫名其妙的问题;而且 SDK 作为相对底层的依赖,对使用方的影响也是无法估量的。所以,不想经常加班改 Bug 的话,就要保证你 SDK 的稳定性。

我们需要注意以下几点:

1. 测试

为了保证每个功能都是正常的,我们可以编写 单元测试(UT)来最大程度上地覆盖 SDK 的功能和代码。

尤其是每次改动代码后、发布新版本之前,都要再完整地执行一遍测试,不要盲目自信。

此外,还可以通过 压力测试 来估算 SDK 的执行效率,比如每秒最多同时执行 3 次、每次要执行 500 毫秒等。建议将这些信息补充到说明文档中,给用户一些预期。当然也可以尝试通过压测来优化 SDK 的性能。

2. 兼容性

重要的函数和接口尽量减少改动,尤其是函数名、入参和返回值!

举个例子,SDK 0.1 版本时,函数的定义是这样的:

boolean isValid(String str)

结果突然在 0.2 版本时改成了:

String checkValid(StringBuilder str)

这样就会导致用户升级时一脸懵逼,怎么报了一堆找不到函数呢?

因此,对于比较大的改动,可以新写一个函数,并且给老函数打上类似 @Deprecated 的注释,表示已过时,引导用户去用新的。

此外,还可以在 版本号 上做做文章,小改动时只改变小版本号,比如 0.0.1 到 0.0.2;大改动时才改变大版本号,比如 1.0 到 2.0。这样可以给用户一个预期:这次改动很大,可能会存在不兼容。

3. 暴露异常

要让用户感知到 SDK 代码中可能抛出的异常,交给他们去进行相应的处理,防止出现一些意料之外的错误。

此外,SDK 要合理地打印日志,尤其是异常日志,在出了问题时要让调用者知道是出了什么问题,便于排查。


以上就是本期分享,建议学编程的同学多自己动手写 SDK,并且按照本文的总结去优化它,对提升编程能力真的很有帮助!

最近整理了我原创的 140 篇编程经验和技术文章,欢迎大家阅读,一起成长!️

指路:https://t.1yb.co/ARnD

我是鱼皮,最后求个 点赞 ,这将是我持续创作的最大动力,谢谢

大厂的 SDK 写法,偷学到了!的更多相关文章

  1. 一个SDK给我干懵逼了?大厂的SDK就这?

    活久见 .org.jboss.netty 和 io.netty 你分的清吗? 大家好,我是小猿来也,一个热衷写 bug 的程序猿. 一天我正在专心致志写 Bug 的时候,一个同事跑过来找我. 说有个很 ...

  2. 忙里偷闲( ˇˍˇ )闲里偷学【C语言篇】——(2)准备知识

    一.变量为什么必须初始化? 在回答这个问题之前,我们先来运行一段代码: #include <stdio.h> int main(){ int i; printf("i=%d\n& ...

  3. div中的img垂直居中的方法,最简单! 偷学来的,,,不要说我抄袭啊(*^__^*)

    让div中的img垂直居中,水平居中很简单,用text-align:center; 让div中img垂直居中的方法其实也很简单 重点是: display:table-cell;   让标签具有表格的属 ...

  4. 忙里偷闲( ˇˍˇ )闲里偷学【C语言篇】——(9)链表

    我们至少可以通过两种结构来存储数据 数组 1.需要一整块连续的存储空间,内存中可能没有 2.插入元素,删除元素效率极低. 3.查找数据快 链表 1.查找效率低 2.不需要一块连续的内存空间 3.插入删 ...

  5. 忙里偷闲( ˇˍˇ )闲里偷学【C语言篇】——(8)枚举、补码

    一.枚举 # include <stdio.h> enum WeekDay //定义了一个数据类型(值只能写以下值) { MonDay, TuesDay, WednesDay, Thurs ...

  6. 忙里偷闲( ˇˍˇ )闲里偷学【C语言篇】——(7)结构体

    一.为什么需要结构体? 为了表示一些复杂的事物,而普通类型无法满足实际需求 二.什么叫结构体? 把一些基本类型组合在一起形成的一个新的复合数据类型叫做结构体. 三.如何定义一个结构体? 第一种方式: ...

  7. 忙里偷闲( ˇˍˇ )闲里偷学【C语言篇】——(6)动态内存分配

    一.传统数组的缺点: 1.数组的长度必须事先定制,且只能是常整数,不能是变量 int len = 5; int a[len]; //error 2.传统形式定义的数组,该程序的内存程序员无法手动释放 ...

  8. 忙里偷闲( ˇˍˇ )闲里偷学【C语言篇】——(5)有趣的指针

    一.指针是C语言的灵魂 # include <stdio.h> int main(){ int *p; //p是变量名,int *表示p变量存放的是int类型变量的地址,p是一个指针变量 ...

  9. 忙里偷闲( ˇˍˇ )闲里偷学【C语言篇】——(4)for == while ?

    一.for和while等价替换 int i = 1; for (i; i<=100; i++){ sum = sum + 1; } int i = 1; while(i<=100){ su ...

随机推荐

  1. HSDB工具类使用探索jvm

    本文是引用https://club.perfma.com/article/2261053 有人问了个小问题,说: public class Test { static Test2 t1 = new T ...

  2. sql select 1 和 exists

    SELECT * FROM LACOMMISION WHERE MANAGECOM LIKE'8694%' AND STATE='1' AND EXISTS(SELECT 1 FROM laagent ...

  3. JDK的安装与配置java环境变量

    JDK安装与配置java环境变量 安装JDK 1.百度搜索jdk8找到下载地址 下载地址:Java SE Development Kit 8 - Downloads (oracle.com) 2.点击 ...

  4. 几篇关于RGBD语义分割文章的总结

      最近在调研3D算法方面的工作,整理了几篇多视角学习的文章.还没调研完,先写个大概.   基于RGBD的语义分割的工作重点主要集中在如何将RGB信息和Depth信息融合,主要分为三类:省略. 目录 ...

  5. user-agent浏览器标识集合

    user_agent_list = [ "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET ...

  6. 数据库比对工具SQL(表、字段、触发器、索引、视图、存储过程)

    做一个数据库比对小工具,把SQL做一个笔记 SELECT object_id AS ID --表ID,'表' sType,Name --表名FROM sys.tablesORDER BY Name-- ...

  7. 虚拟基站(VRS)

      虚拟参考站技术(Virtual Reference Station,简称VRS)也称虚拟基准站技术,是一种网络实时动态测量实时动态测量(RTK)技术,通过在某一区域内建立构成网状覆盖的多个GPS基 ...

  8. RHCSA_DAY12

    Linux软件包的分类 inghu 源码包 二进制包(RPM包) 源码包特点 源码包缺点:安装过程麻烦,需要用户手动编译,需要手动解决软件包的依赖关系 源码包优点:软件源代码开放,允许用户二次开发,安 ...

  9. 爱了,字节跳动大神最佳整理:582页Android NDK七大模块学习宝典,理论与实践

    前言 时至今日,短视频App可谓是如日中天,一片兴兴向荣.随着短视频的兴起,音视频开发也越来越受到重视,而且薪资水涨船高,以一线城市为例,音视频工程开发的薪资比Android应用层开发高出40%. 但 ...

  10. String s="a"+"b"+"c",到底创建了几个对象?

    首先看一下这道常见的面试题,下面代码中,会创建几个字符串对象? String s="a"+"b"+"c"; 如果你比较一下Java源代码和反 ...