缘由
最近参与的项目(C++写的)已经算是进入维护阶段,新的项目也是移植之前项目的代码, 在其基础上修改的。在不断深入熟悉代码的过程中,发现很多重复的代码,而且很多代码 可以通过重构来增加可读性。一来可以减少日后的维护成本,二来可以降低新加入项目组的 成员的脑细胞损耗程度。;)
可是想了很久依然不敢动手,改一点代码都战战兢兢,生怕是用错了某个接口 API (C++ 类或者函数)而引入新的 Bug。 因为发现一个 bug 需要花时间去查找,更需要话时间去测试,一旦遇上了和其他 专业扯到一块的问题,那可准备好两三天时间捉虫了。人与人的沟通成本本来就很高, 再加上跨专业联调那可真要命了。
究其原因,是因为
- 接口没有文档说明。有文档说明也不同步。
- 接口的命名看了实在让人感到一头雾水,不知所云。
- 接口的行为没有说明清楚,当想用它是不知道会不会有什么特殊的情况需要处理。
- 接口没有遵循单一职责的原则,存在很多看似相似但有不知道有什么区别的接口。
- 当接口被很多模块引用时,改一点都会有牵一发而动全身的效应。
我的看法
- 接口应该精心谨慎地设计,遵循单一职责的原则。
- 接口必须是稳定的,一旦发布就很少有改动的机会,因为别人已经依赖它了。 Windows API 一直都在增加,被删除的似乎很少,最多会被标记为 departed, 并被新的 API 替代。CPU 指令也是一种接口,如 x86 指令集,它们的数量也一直在增加, 但旧的指令并没有因此而被删除。
- 接口应该有良好的文档说明,特别是以二进制的形式发布的接口更应该如此。
倘若接口发布时能获取相应的源码而没有良好的文档化,大多数用户都会去查看它的源码, 已了解它的行为和各种异常情况。也许这个过程所花费的精力和时间足以再去重写一次了。 《Google C++ Style Guide》建议如果函数因为某种错误而崩溃,则函数名以OrDie为后缀, 这应该算是最佳实践了。它也建议不使用 C++ 异常,这条原则也值得借鉴。C++ 允许抛出 任何类型的异常,并不像 C# 那样规定所有都必须继承至 Exception 类,导致很难用 合适的方法去捕获。想用 catch(…) ? 这会把有价值的异常信息吞掉。 原本一个异常可以容许发生,但不会因此而结束整个应用程序, 但漏掉它可能导致整个应用程序退出。但不可否认异常能带来很多的好处,所以使用 异常之前每个人应该心里有数,规定使用的原则,并为可能抛出异常的接口写好文档。
如果接口以二进制的形式发布而且缺乏文档,那还是别用了吧,潜在的威胁比带来的好处要 多得多。