你为什么也应该写一个开源库?
原文: 你为什么也应该写一个开源库?
之前工作折腾了一年多SDK,到现在公司也是先做SDK再做App,所以从SDK的角度来说,我被人“坑”过,也坑过别人,在这块思考得挺多,简单说说我的一些看法。首先很多人看到这个问题的时候,肯定都会觉得,写库是牛人干的事情,我之前也不例外的这样认为,但是最近开始尝试写了几个简单的NPM包,我发现上面的逻辑很可能反了,可能是写库让他们变牛或者变更牛。当然,如果你是Android开发,你的库可能是AAR,如果iOS那应该是framework,下文提到的“库”就作为各种公共可供第三方复用的独立模块的代称。
0. 写库逼迫你解耦和抽象
我们在软件工程学习的过程中,就无数次的被强调软件模块的设计要高内聚低耦合,然而大多数情况这只是停留在说说的基础上,很多人的代码如果要抽离,是非常困难的,经常会有公共的工具库里面带有业务逻辑的情况,也就是这些代码无法直接拿出来做库供别人用的,即是说这份代码是高耦合的。
写业务逻辑的思路是,写一份代码来满足当前需求。写库的思路是,我要实现一个功能,它可以让某个业务需求的实现更简单,同时他具有通用性。通常情况我们是从业务代码里面提公用库,这个过程就是解耦和抽象的过程,目标就是让代码更通用,对外界依赖更少,职责更纯粹。这样的事做多了,我相信你自己的代码即使不抽成公共库,也能比以前耦合更低。
可以参考《重构 改善既有代码的设计》来学习如何提取和抽象公用的部分。
1. 单一职责
之前我一个同事问我,我们代码库里面为什么没有common这样的目录,他要用于放置一些公用的东西,确实,我在设计项目目录结构的过程中刻意的避免了防止common这样的目录,除了common,还有一些叫global、utils或者base这样的名字,我是非常反感的,因为非常多的东西容易被放进去,而且,他们只有一个共同特点,那就是他们是被共用的,仅此而已,但是这也算共同特点吗?一个更好的划分应该是根据功能,例如,这个类是处理日期的,那个类是处理金额的,另外一个类是处理格式校验的。如果你抽取一个库,我像你不会想写一个common的库吧,按功能给它命名,因为只有这样,才容易让别人知道你的库是干什么的,也才可能会被别人用到。如果不抽离出来,你的common目录会越来越common,common到非常难用。
3. 深入思考接口的设计
库的接口一旦提供,有其他人用了,你就不能随意变更,每次修改都应该考虑向前兼容。这就要求前期设计接口时就要充分思考,而不是随意的写一个。这里需要考虑的有接口名称,单个接口名称和多个接口名称是否能一致,例如,例如断然不要写一个signup接口和一个logout接口作为一对,用signIn/signUp/signOut会好很多,其次,参数个数也应该考虑,每个参数都是对外界的耦合,随意加参数只会让接口更难用,设计接口的过程中可以考虑一下,这样设计的接口文档好写吗?如果你的接口要用大段文字说明它的用法,那我相信它不会是一个优雅的接口。
2. 文档的编写
上面一段提到文档编写,一个库通常要包含几部分详细的说明,如何集成,如果配置和初始化,各个接口如何使用。就像我上面说的那样,在设计接口时候通过思考文档是否复杂来从侧面评估接口是否优雅,所以文档的编写其一是为了帮助别人使用,其二是对整个库设计从使用者角度的再一次审视。日常工作中可能会做类似的抽取公共库的事情,但是因为限于团队内部,经常会忽略很多细节依赖,而这些依赖正是让你的公共模块无法复用,难以使用的罪魁祸首。
4. 对修改的封闭
之前项目里偶有发生某人改了公共目录下的内容,导致其他地方出错,聊过的大部分项目都是通过定军规来做,xxx目录修改必须通过评审,我始终觉得,没有控制彻底和没控制区别不大,真要设置不让人随意修改,就应该分离出来,单独成模块。这个模块库的代码权限直接控制,这样100%保证不会被“实习生”修改。如果按上面的思路把公用的部分抽取出来,可以保证公共模块更加稳定,当然这也得益于抽取路过程中的解藕和抽象。
5. 自己写库让我们更懂得感恩
乍听起来这有点偏题,其实最主要想说的就是:“养儿方知父母恩”。我想如果你认真的去写了一个库,你能更深刻的理解写库的辛苦付出,可能你以后发现某些库文档不完善的时候,你会更宽容一些,看到作者因为经历不足停止维护时候你也会多一些理解。总之,在你只用别人写的库的时候,你一定不能体会到开源库作者们的辛苦付出的,写到这里,我真心的感谢他们的付出。