经常使用的类比(包括在维基百科页面上,我注意到)是让狗走路的类比 - 你会问狗,你不会要求访问它的腿然后让它的腿走路。
让狗走路是一种更好的解耦方式,因为有一天你可能想要一只没有腿的狗。
在您的具体示例中,m_A
的实现可能不再依赖于B
.
编辑:因为有些人想要进一步说明,让我试试这个:
如果对象X
包含该语句m_A.GetObjectB().DoSomething()
,则X
必须知道:
- 具有通过 暴露
m_A
的对象的实例;和B
GetObject()
- 对象
B
有方法DoSomething()
。
所以X
需要知道 和 的接口,并且A
必须始终能够出售。B
A
B
相反,如果X
只是必须做,m_A.DoSomething()
那么它只需要知道:
- 那个
m_A
有方法DoSomething()
。
因此,法律有助于脱钩,因为X
现在已经完全脱钩了B
——它不需要任何关于那个阶级的知识——并且对它的了解更少A
——它知道A
可以实现DoSomething()
,但它不再需要知道它是否自己做到了,或者它是否要求某人否则去做。
在实践中,通常不使用该定律,因为它通常只意味着编写数百个包装函数,例如,并且A::DoSomething() { m_B.DoSomething(); }
程序的形式语义通常明确规定A
将具有履行该对象与整个系统的契约。B
GetObjectB()
第一点也可以用来论证该定律增加了耦合。假设您最初拥有m_A.GetObjectB().GetObjectC().GetObjectD().DoSomething()
并且您已将其折叠为m_A.DoSomething()
. 那意味着因为C
知道D
实现DoSomething()
,C
所以必须实现它。然后因为B
现在知道C
实现了DoSomething()
,B
就必须实现它。等等。最后,您必须A
实施DoSomething()
,因为D
确实如此。所以A
最终不得不以某些方式行动,因为D
以某些方式行动,而以前它可能一无所知D
。
在第一点上,一个类似的情况是 Java 方法传统上声明它们可以抛出的异常。这意味着他们还必须列出他们调用的任何东西如果没有捕获它可能抛出的异常。因此,每次叶方法添加另一个异常时,您都必须沿着调用树将异常添加到一大堆列表中。因此,一个好的脱钩想法最终会产生无休止的文书工作。
关于第二点,我认为我们误入了“是”与“有”的辩论。'Has a' 是表达一些对象关系的一种非常自然的方式,并且教条地将其隐藏在“我有储物柜钥匙,所以如果你想打开你的储物柜,就来问我,我会解锁它”的表面之下 - 类型谈话只是模糊了手头的任务。