New Metric: the Distance of Coupling

The following text is a partial translation of the original English article, performed by ChatGPT (gpt-3.5-turbo) and this Jekyll plugin:

封装,正如您所知,是面向对象编程中的四个关键原则之一。根据Grady Booch等人的说法,封装是“隐藏对象中不对其基本特征做出贡献的所有秘密的过程。”从实际角度来看,它涉及到我们在Java和C++中使用的那些“private”属性:它们对我们对象的用户不可见,因此它们无法被修改甚至读取。Booch等人认为封装的目的是“在不同的抽象之间提供明确的屏障”,从而实现“关注点的明确分离”。然而,它是否真正按计划工作?我们是否真的在对象之间有明确的屏障?让我们看一下。

首先,我并不是第一个也不是唯一一个提出这个问题的人。David West早些时候说过,“在大多数情况下,封装更像是一种纪律而不是真正的屏障”,并且“很少有对象的完整性在任何绝对意义上受到保护”。实际上,“尊重对象的封装是对象的使用者的责任。”确实,让我们来看一下我关于裸数据的博客文章中的Temperature类。

我们可以说属性t是真正的封装吗?从技术上讲,是的:无法通过点表示法直接修改它。简而言之,我们不能这样做:

而我们甚至都不能做到这一点:

然而,我们可以通过getter getT()和setter setT()完全做到同样的事情。因此,类Temperature的设计者赋予了我们通过getter和setter间接访问其属性的能力。我会说这里违反了封装的原则,我确信Allen Holub会同意我的观点。解决办法是什么呢?裸露数据的文章提出了使用TellDontAsk原则,并且建议我们摒弃getter方法:

现在 Temperature 类不允许我们读取它的属性 t。相反,我们只能告诉它准备一个温度的字符串表示并将其返回给我们。也许不完全是“告诉”范例的经典例子,因为一些数据会返回,但现在它看起来比之前好多了。这种重构的美妙之处在于客户端和对象之间的耦合更少。通过获取器(或通过点表示法直接访问属性),客户端能够检索温度的数值并将其从摄氏度重新计算为华氏度。通过返回 String,客户端将不再这样做。该字符串只会被用作最终产品,不可修改。或者也许不是这样?

如果客户端这样做会怎样:

现在看起来怎么样?这不是违反封装的吗?toString()的结果没有按照预期的方式处理。不是作为一个固定的字符串,而是作为具有某种内部结构的数据,这是已知的客户端。客户端对输出的知识是关键问题。客户端知道得太多,并且利用这些知识来自己的利益:解构数据并操作结果。

我们真的可以禁止客户端这样做吗?据我所知,任何编程语言都没有这样的功能。当方法的输出传递给客户端时,客户端可以根据需要进行任何操作。如果我正确理解了韦斯特博士的话,这就是封装的不尊重。而且我们甚至还没有讨论反射API,它可以允许我们在不调用任何方法的情况下从Temperature中取出t

因此,封装不是一个显式的屏障。只要我们有尊重封装的愿望,它就存在。如果我们不尊重封装,任何人都可以任意滥用对象。而且private属性修饰符也无济于事。而且,它们只会创造出封装的错觉,而实际上每个人都可以根据他们的业务需求做任何他们觉得合适的事情。

不过,我有一个提议,这可能有助于我们更明确地实现封装。

如果我们能够控制将数据和对象返回给客户端后发生的情况,那会怎样呢?如果我们可以阻止客户端对toString()方法的输出进行split()操作,我们可以在编译时通过检查所有代码来实现这一点,看看我们的对象与它们返回的地方之间的交互时机有多远。在上面的示例中,距离为两个:1)首先,我们执行split,2)其次,我们执行parseInt()。当然,更大的应用程序会有更大的数字。

似乎我们可以使用这个距离数字作为整个应用程序中对象之间耦合的度量标准。数字越大(或所有数字的平均值越大),设计越糟糕:在良好的设计中,我们不应该从一个方法中取出某些东西,然后进行一些复杂的处理。根据上面提到的TellDontAsk原则,我们应该让我们的对象做工作,并只返回一个快速的摘要。距离度量将准确告诉我们:我们违反了松耦合原则的次数和程度。

您有兴趣为Java代码创建这样的分析器吗?

Translated by ChatGPT gpt-3.5-turbo/42 on 2023-12-16 at 15:10

sixnines availability badge   GitHub stars