缘由

最近参与的项目(C++写的)已经算是进入维护阶段,新的项目也是移植之前项目的代码, 在其基础上修改的。在不断深入熟悉代码的过程中,发现很多重复的代码,而且很多代码 可以通过重构来增加可读性。一来可以减少日后的维护成本,二来可以降低新加入项目组的 成员的脑细胞损耗程度。;)

可是想了很久依然不敢动手,改一点代码都战战兢兢,生怕是用错了某个接口 API (C++ 类或者函数)而引入新的 Bug。 因为发现一个 bug 需要花时间去查找,更需要话时间去测试,一旦遇上了和其他 专业扯到一块的问题,那可准备好两三天时间捉虫了。人与人的沟通成本本来就很高, 再加上跨专业联调那可真要命了。

究其原因,是因为

  1. 接口没有文档说明。有文档说明也不同步。
  2. 接口的命名看了实在让人感到一头雾水,不知所云。
  3. 接口的行为没有说明清楚,当想用它是不知道会不会有什么特殊的情况需要处理。
  4. 接口没有遵循单一职责的原则,存在很多看似相似但有不知道有什么区别的接口。
  5. 当接口被很多模块引用时,改一点都会有牵一发而动全身的效应。

我的看法

  1. 接口应该精心谨慎地设计,遵循单一职责的原则。
  2. 接口必须是稳定的,一旦发布就很少有改动的机会,因为别人已经依赖它了。 Windows API 一直都在增加,被删除的似乎很少,最多会被标记为 departed, 并被新的 API 替代。CPU 指令也是一种接口,如 x86 指令集,它们的数量也一直在增加, 但旧的指令并没有因此而被删除。
  3. 接口应该有良好的文档说明,特别是以二进制的形式发布的接口更应该如此。
    倘若接口发布时能获取相应的源码而没有良好的文档化,大多数用户都会去查看它的源码, 已了解它的行为和各种异常情况。也许这个过程所花费的精力和时间足以再去重写一次了。 《Google C++ Style Guide》建议如果函数因为某种错误而崩溃,则函数名以OrDie为后缀, 这应该算是最佳实践了。它也建议不使用 C++ 异常,这条原则也值得借鉴。C++ 允许抛出 任何类型的异常,并不像 C# 那样规定所有都必须继承至 Exception 类,导致很难用 合适的方法去捕获。想用 catch(…) ? 这会把有价值的异常信息吞掉。 原本一个异常可以容许发生,但不会因此而结束整个应用程序, 但漏掉它可能导致整个应用程序退出。但不可否认异常能带来很多的好处,所以使用 异常之前每个人应该心里有数,规定使用的原则,并为可能抛出异常的接口写好文档。
    如果接口以二进制的形式发布而且缺乏文档,那还是别用了吧,潜在的威胁比带来的好处要 多得多。