1

我的应用程序中有一些这样的代码。它写出一些 XML:-

public void doStuff( Business b, XMLElement x)
{
   Foo f = b.getFoo();
   // Code doing stuff with f
   // b is not mentioned again.
}

据我了解,摄魂怪法则会说这很糟糕。“代码完成”说这是增加耦合。这种方法首先应该采用“f”。

public void doStuff( Foo f, XMLElement x)
{
    // Code doing stuff with f
}

但是,现在我来更改此代码,我确实需要在b.

public void doStuff( Business b, XMLElement x)
{
   Foo f = b.getFoo();
   // Code doing stuff with f
   // A different method is called on b.
}

由于更改完全在方法内部,因此该接口使生活变得更轻松。我不必担心从应用程序周围调用它的许多地方。

这向我表明原始设计是正确的。你同意?我错过了什么?

PS。我不认为该行为属于 b 本身,因为域对象不知道该系统中作为 XML 的外部表示。

4

4 回答 4

4

首先,这不一定违反得墨忒耳定律,除非您实际上是在 doStuff 中调用 Foo 对象 f 上的方法。如果你不是,那么你可能没问题;您只使用业务对象 b 的接口。所以我假设你在'f'上调用了至少一种方法。

您可能“缺少”的一件事是可测试性,特别是单元测试。如果你有:

public void doStuff( Business b, XMLElement x)
{
    Foo f = b.getFoo();
    // stuff using f.someMethod
    // business stuff with b
    // presumably something with x
}

...然后如果你想测试 doStuff 对不同的 Foo 做正确的事情,你必须首先用你想要的每个 Foo 'f' 创建(或模拟)一个新的 Business 对象,然后将该对象插入 doStuff (甚至如果其他特定于业务的东西是相同的)。您正在从您的方法中删除一个测试,虽然您的源代码可能保持简单,但您的测试代码变得更加混乱。所以,如果你真的需要 doStuff 中的 f 和 b,那么可以说它们都应该是参数。

欲了解更多信息,此人是我遇到的最强烈的得墨忒耳法则活动家之一;经常为它提供理由

于 2009-11-26T07:15:47.963 回答
2

我认为很难给你一个明确的答案,因为

1)问题陈述非常抽象,
2)没有“绝对”的好设计——它也取决于你的类周围有什么,最初的好设计可能会随着系统的增长而演变成你想要重构的东西发展,你对领域的理解变得更加精细。

我不认为第一个示例是对 Demeter 原则的“大规模”违反,但是一切都在细节中,这取决于您的评论部分中发生了多少 - 如果您需要,您可以随时添加更多间接性. 例如,您可以在 WriteBusinessObjectToXmlService 类上使用您的方法“DoStuff”,如果涉及 f 的工作量正在增长,您可以将其提取到它的方法“DoStuffWithF(f, x)”中,甚至创建一个单独的类 WriteFToXmlService,带有 DoStuff(f, x)。

于 2009-11-26T05:37:30.750 回答
1

如果我们进一步遵循这个逻辑,我们将提出一个想法,即应该使用包含指向系统中所有内容的链接的 global-everything-objects-repository 对象(或服务定位器)。我们根本不需要更改方法签名,因为我们只需要这个存储库。

问题是方法的目的已经改变,但签名没有。如果 Foo 是该方法所需的一切,那么它应该只接受 Foo 。这样我们就可以看出它只在 Foo 上运行。这将更清楚地传达该方法的目的。如果它突然也需要业务,我们需要更改方法签名,因为它应该指示其他方法的目的和要求

于 2009-11-26T05:27:43.347 回答
0

也许现在传入 Business 是合理的,或者该方法需要第三个参数:在 Business 对象上调用的另一个方法的返回类型。这取决于 doStuff 方法主体的其余部分。

于 2009-11-26T05:31:56.360 回答