首页 > 解决方案 > 耦合 - 除了更改方法签名或返回类型之外,更改一个模块如何影响另一个模块?

问题描述

在高耦合环境中,更改一个模块会影响另一个模块。好的,但我看不出这怎么可能(除了更改方法签名或返回类型)?

好吧,如果我改变一个类,那么它只能在以下情况下破坏其他类中的代码:

  1. 如果我突然改变了一个方法的返回类型——那么我将不得不去另一个类并修复类型的不匹配。
  2. 如果我更改方法签名 - 那么我将不得不转到所有依赖类并在调用更改的方法的任何地方更改方法参数。

出于同样的原因,依赖抽象(接口)是很好的,这样我们就可以保证定义的方法会在那里。

除此之外,更改一个类还能如何影响另一个依赖类?

标签: oopdesign-patternsinterfacedependencieslanguage-agnostic

解决方案


即使这被标记为“Java”,我也会给出一个更广泛的答案。

耦合用模块 A 在使用模块 B 时做出的假设来表示。模块 A 做出的假设越多,它与模块 B 的耦合就越多。

一个模块的 API 基本上是它的消费者可以做出的一组假设。返回类型或方法签名只是编译器可以验证的假设。

例如,SimCity 与 Windows 3.x 内存分配器相结合。内存分配器有一个合同,说你可以假设 X、Y 和 Z,就是这样。不能假设任何其他行为。但 SimCity 假设了这一点,因此,他们无法更改 Windows 95 中的内存分配器(嗯,不是在 SimCity 执行时)。

显然这不是有意的耦合——任何关于模块超出其接口的假设都应该是无意的,但这仍然是一个假设。

其他示例包括:

  • 一个密码哈希 API,它告诉它的消费者哈希算法是什么。然后,消费者可能会将散列密码的长度硬编码到数据库模式中,这意味着只有在数据库迁移之后才能将其更改为具有更长散列的更安全算法。
  • 每个使用 protobuf 的人不仅与它的 API 耦合,还与它的有线格式耦合。如果 Google 想要更改有线格式,它将破坏生产者或消费者尚未升级的所有部署。
  • 如果您使用例如GCC 的 GNU 编译器扩展,那么您将与 GCC 耦合。如果您想(或必须)使用另一个编译器,您最好希望它们也恰好实现相同的扩展。
  • 每个程序都与它的编程语言耦合。例如,如果 Oracle 想要更改 Java 以使函数重载成为不可能,例如由于某些致命的设计错误(100 年才出现一次),那么所有 Java 程序都将停止编译。
  • 如果你编写一个 POSIX 程序,你依赖于 POSIX 的实现。如果您的 POSIX 程序使用POSIX basename(3),但您的实现破坏了它的语义,那么您的程序就不能在这个实现上工作(GNU 引入了一个兼容性宏来避免这种情况)。

推荐阅读