Demeter违规法证明是有用的。我错过了什么吗?

时间:2009-11-26 05:04:38

标签: language-agnostic law-of-demeter

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

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

据我了解,Dementer法则会说这很糟糕。 “代码完整”说这是增加耦合。这种方法首先应该采用“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 个答案:

答案 0 :(得分:4)

首先,这不一定是Demeter违规法,除非你实际上在doStuff中调用Foo对象f上的方法。如果你不是,那么你可能很好;您只使用Business对象的界面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,那么可以说它们都应该是参数。

如需更多阅读,this person是我遇到过的最强调的Law of Demeter活动家之一;经常为它提供rationales

答案 1 :(得分:2)

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

1)问题陈述非常抽象,而且 2)没有“绝对”好的设计 - 它取决于你的课程周围的内容,最初的优秀设计可能演变成你想要重构的东西,随着你的系统的发展和演变,你对领域的理解变得更多精制。

我没有看到第一个例子是对Demeter原理的“大规模”违规,但是所有细节都在细节上,这取决于你评论部分的进展情况 - 你可以随时添加更多的间接你需要。例如,您可以在WriteBusinessObjectToXmlService类上使用您的方法“DoStuff”,如果涉及f的工作量正在增长,您可以将其提取到其方法“DoStuffWithF(f,x)”中,或甚至创建一个单独的类WriteFToXmlService,带有DoStuff(f,x)。

答案 2 :(得分:1)

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

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

答案 3 :(得分:0)

现在可能有理由传入Business或该方法需要第三个参数:在Business对象上调用的另一个方法的返回类型。它取决于doStuff方法的其余部分。