我们很少能够完全控制我们系统中的所有软件。有时我们会购买第三方软件包或使用开源软件。其他时候,我们依赖于公司内部的团队为我们生产组件或子系统。无论如何,我们必须将这些外来代码与我们自己的代码清晰地集成。

使用第三方代码

接口的提供者和使用者之间存在一种天然的张力。第三方软件包和框架的提供者力求广泛适用,以便它们能够在许多环境中工作,并吸引广泛的受众。另一方面,用户希望有一个专注于他们特定需求的接口。这种张力可能会在我们的系统的边界处引起问题。例如:

  1. Map sensors = new HashMap();
  2. Sensor s = (Sensor)sensors.get(sensorId);

  1. public class Sensors {
  2. private Map sensors = new HashMap();
  3. public Sensor getById(String id) {
  4. return (Sensor) sensors.get(id);
  5. }
  6. // 其余代码
  7. }

第一段代码在 Map 中暴露了类型转换,而第二段代码能够以对应用程序影响很小的方式演进。类型转换和管理在 Sensors 类内部处理。

接口也被定制和限制,以满足应用程序的需求。这导致代码更容易理解,更难以滥用。Sensors 类可以强制执行设计和业务规则。

探索和学习边界

第三方代码帮助我们在更短的时间内交付更多功能。当我们想要使用某个第三方软件包时,我们应该从哪里开始?测试第三方代码不是我们的工作,但编写我们使用的第三方代码的测试可能最符合我们的最佳利益。

编写一些测试来学习和理解如何使用第三方代码是一个好主意。Newkirk 称这类测试为学习测试。

学习测试比免费更好

学习测试最终没有成本。我们无论如何都需要学习 API,编写这些测试是获得该知识的一个简单且隔离的方式。学习测试是精确的实验,有助于提高我们的理解。

不仅学习测试是免费的,它们还有积极的投资回报。当第三方软件包有新版本发布时,我们运行学习测试来看看是否存在行为差异。

使用尚不存在的代码

有时,我们需要在一个模块中工作,该模块将与另一个正在开发的模块连接,我们不知道如何发送信息,因为 API 尚未设计。在这种情况下,建议创建一个接口来封装与待定模块的通信。这样,我们可以控制我们的模块,即使第二个模块尚不可用,我们也可以进行测试。

清晰的边界

有趣的事情在边界处发生。变化就是其中之一。好的软件设计能够在不需要巨大投资和重做的情况下适应变化。当我们使用我们无法控制的代码时,必须特别注意保护我们的投资,并确保未来的变更不会过于昂贵。