我一直在阅读有关“Demeter法则”的文章,并且它(通常是纯“包装”类)似乎通常是反模式。考虑一个实现类:
class FluidSimulator {
void reset() { /* ... */ }
}
现在考虑另一个类的两种不同实现:
class ScreenSpaceEffects1 {
private FluidSimulator _fluidDynamics;
public FluidSimulator getFluidSimulator() { return _fluidDynamics; }
}
class ScreenSpaceEffects2 {
private FluidSimulator _fluidDynamics;
public void resetFluidSimulation() { _fluidDynamics.reset(); }
}
以及调用所述方法的方法:
callingMethod() {
effects1.getFluidSimulator().reset(); // Version 1
effects2.resetFluidSimulation(); // Version 2
}
乍一看,版本2似乎更简单一些,并且遵循“Demeter规则”,隐藏Foo的实现等。但这将FluidSimulator中的所有更改与ScreenSpaceEffects联系在一起。例如,如果添加了一个参数以进行重置,那么我们可以:
class FluidSimulator {
void reset(bool recreateRenderTargets) { /* ... */ }
}
class ScreenSpaceEffects1 {
private FluidSimulator _fluidDynamics;
public FluidSimulator getFluidSimulator() { return _fluidDynamics; }
}
class ScreenSpaceEffects2 {
private FluidSimulator _fluidDynamics;
public void resetFluidSimulation(bool recreateRenderTargets) { _fluidDynamics.reset(recreateRenderTargets); }
}
callingMethod() {
effects1.getFluidSimulator().reset(false); // Version 1
effects2.resetFluidSimulation(false); // Version 2
}
在两个版本中,callingMethod都需要更改,但是在版本2中,ScreenSpaceEffects也需要更改。有人可以解释一下使用包装器/外观的好处(适配器,包装外部API或公开内部API除外)。
编辑:我遇到了许多实际示例,而不是琐碎的示例。
最佳答案
主要区别在于,在版本1中,作为Bar
抽象的提供者,您无法控制Foo
的公开方式。 Foo
的任何更改都将向您的客户公开,他们将不得不接受。
在版本2中,作为抽象Bar
的提供者,您可以决定是否以及如何公开演变。它仅取决于Bar
的抽象,而不取决于Foo
的抽象。在您的示例中,您的Bar
抽象可能已经知道要作为参数传递哪个整数,因此您将能够让您的用户透明地使用新版本的Foo
,而无需进行任何更改。
假设现在Foo不断发展,并且要求用户先调用foo.init()
,然后再调用doSomething
。在版本1中,Bar的所有用户都将需要查看Foo的更改,并修改其代码。对于版本2,只需更改Bar
,如果需要,其doSomething
会调用init
。这导致更少的错误(仅抽象Bar
的作者必须了解和理解抽象Foo
并减少了类之间的耦合。
关于design-patterns - 包装器/得墨meter耳的法律似乎是一种反模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2550961/