首先,这不一定违反德米特法则,除非您实际上是在 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,那么可以说它们都应该是参数。
First thing is that this isn't necessarily a Law of Demeter violation, unless you are actually calling methods on the Foo object f in doStuff. If you are not, then you are probably fine; you're only using the interface of the Business object b. So I'll assume that you are calling at least one method on 'f'.
One thing you might be "missing" is testability, specifically unit tests. If you have:
public void doStuff( Business b, XMLElement x)
{
Foo f = b.getFoo();
// stuff using f.someMethod
// business stuff with b
// presumably something with x
}
... then if you want to test that doStuff does the right thing for different Foo, you have to first create (or mock) a new Business object with each Foo 'f' that you want, then plug that object into doStuff (even if the rest of the Business-specific stuff is identical). You're testing at one remove from your method, and while your source code may stay simple, your test code gets messier. So, if you really need both f and b in doStuff, then arguably they should both be parameters.
For more reading on it, this person is one of the most emphatic Law of Demeter campaigners I've come across; frequently provides rationales for it.
I think it's difficult to give you a clear-cut answer because
1) the problem statement is pretty abstract, and 2) there is no "absolute" good design - it depends on what's around your classes too, and what was a good design initially might evolve into something you want to refactor as your system grows and evolves, and your understanding of the domain becomes more refined.
I don't see the first example as a "massive" violation of the Demeter principle, but again everything is in the details, it depends on how much is going on in your commented section - you can always add more indirection if you need to. You could for instance have your method "DoStuff" on a WriteBusinessObjectToXmlService class, and if the amount of work you were doing involving f was growing, you could extract it into its method "DoStuffWithF(f, x)", or even create a separate class WriteFToXmlService, with DoStuff(f, x).
If we follow with this logic further we will come up with an idea that a global-everything-objects-repository object (or service locator) should be used which contains links to everything in the system. Than we will not need to change method signatures at all because this repository is all we need.
The problem is that the purpose of the method has changed, but the signature has not. If Foo is everything the method needs than it should accept Foo only. This way we can tell that it operates solely on Foo. This will communicate the purpose of the method more clearly. If it suddenly needs Business too we need to change the method signature because it should indicate other method purpose and requirements
也许现在传入 Business 是合理的,或者该方法需要第三个参数:在 Business 对象上调用的另一个方法的返回类型。它取决于 doStuff 方法主体的其余部分。
Maybe it is now justified to pass in Business or the method needs a third parameter: the return type of the other method called on the Business object. It depends on the rest of the body of the doStuff method.
发布评论
评论(4)
首先,这不一定违反德米特法则,除非您实际上是在 doStuff 中调用 Foo 对象 f 上的方法。如果不是,那么你可能没问题;您只使用业务对象的接口 b.所以我假设您在“f”上调用至少一种方法。
您可能“缺少”的一件事是可测试性,特别是单元测试。如果您有:
...那么如果您想测试 doStuff 对不同的 Foo 执行正确的操作,您必须首先使用您想要的每个 Foo 'f' 创建(或模拟)一个新的 Business 对象,然后插入该对象进入 doStuff(即使业务特定的其余内容是相同的)。您正在从方法中进行一次删除测试,虽然您的源代码可能保持简单,但您的测试代码会变得更加混乱。因此,如果您确实在 doStuff 中同时需要 f 和 b,那么可以说它们都应该是参数。
如需了解更多内容,此人是最强调的我遇到过的德米特法则活动人士;经常提供理由。
First thing is that this isn't necessarily a Law of Demeter violation, unless you are actually calling methods on the Foo object f in doStuff. If you are not, then you are probably fine; you're only using the interface of the Business object b. So I'll assume that you are calling at least one method on 'f'.
One thing you might be "missing" is testability, specifically unit tests. If you have:
... then if you want to test that doStuff does the right thing for different Foo, you have to first create (or mock) a new Business object with each Foo 'f' that you want, then plug that object into doStuff (even if the rest of the Business-specific stuff is identical). You're testing at one remove from your method, and while your source code may stay simple, your test code gets messier. So, if you really need both f and b in doStuff, then arguably they should both be parameters.
For more reading on it, this person is one of the most emphatic Law of Demeter campaigners I've come across; frequently provides rationales for it.
我认为很难给你一个明确的答案,因为
1)问题陈述非常抽象,并且
2)不存在“绝对”的好的设计——这也取决于你的类周围的东西,随着你的系统的成长和发展,以及你对领域的理解变得更加深入,最初的好设计可能会演变成你想要重构的东西精制。
我不认为第一个例子是对 Demeter 原则的“大规模”违反,但一切都在细节中,这取决于您的评论部分中发生了多少事情 - 如果需要,您可以随时添加更多间接内容。例如,您可以在 WriteBusinessObjectToXmlService 类上使用方法“DoStuff”,如果涉及 f 的工作量不断增长,您可以将其提取到其方法“DoStuffWithF(f, x)”中,甚至创建一个单独的方法WriteFToXmlService 类,带有 DoStuff(f, x)。
I think it's difficult to give you a clear-cut answer because
1) the problem statement is pretty abstract, and
2) there is no "absolute" good design - it depends on what's around your classes too, and what was a good design initially might evolve into something you want to refactor as your system grows and evolves, and your understanding of the domain becomes more refined.
I don't see the first example as a "massive" violation of the Demeter principle, but again everything is in the details, it depends on how much is going on in your commented section - you can always add more indirection if you need to. You could for instance have your method "DoStuff" on a WriteBusinessObjectToXmlService class, and if the amount of work you were doing involving f was growing, you could extract it into its method "DoStuffWithF(f, x)", or even create a separate class WriteFToXmlService, with DoStuff(f, x).
如果我们进一步遵循这个逻辑,我们将提出一个想法,即应该使用全局所有对象存储库对象(或服务定位器),其中包含系统中所有内容的链接。我们根本不需要更改方法签名,因为这个存储库就是我们所需要的。
问题是方法的目的已经改变,但签名没有改变。如果 Foo 是该方法所需的一切,那么它应该只接受 Foo。这样我们就可以知道它仅在 Foo 上运行。这将更清楚地传达该方法的目的。如果它突然也需要业务,我们需要更改方法签名,因为它应该表明其他方法的目的和要求
If we follow with this logic further we will come up with an idea that a global-everything-objects-repository object (or service locator) should be used which contains links to everything in the system. Than we will not need to change method signatures at all because this repository is all we need.
The problem is that the purpose of the method has changed, but the signature has not. If Foo is everything the method needs than it should accept Foo only. This way we can tell that it operates solely on Foo. This will communicate the purpose of the method more clearly. If it suddenly needs Business too we need to change the method signature because it should indicate other method purpose and requirements
也许现在传入 Business 是合理的,或者该方法需要第三个参数:在 Business 对象上调用的另一个方法的返回类型。它取决于 doStuff 方法主体的其余部分。
Maybe it is now justified to pass in Business or the method needs a third parameter: the return type of the other method called on the Business object. It depends on the rest of the body of the doStuff method.