5

我最近在 Robert.C.Martin 的优秀著作 Agile Principals, Patterns and Practices in C# 中读到了 Dependency-Inversion Principal。然而,我觉得我不完全理解这个原则的一个方面。

Robert 解释说,当高层模块依赖于低层模块时,低层模块的变化也会导致高层模块发生变化。他通过以下示例演示了这一点:

public class Button
{
   private Lamp lamp;
   public void Poll(){
      if(/*some condition*/)
         Lamp.TurnOn();
   }
}

关于这段代码,Robert 说“Button 类直接依赖于 Lamp 类。这种依赖意味着 Button 将受到 Lamp 更改的影响。”

在我看来,我们可能会对 Lamp 类进行两种可能的更改:

1)我们可能想改变类的内部实现但不影响公共接口。

2) 我们可能决定更改公共接口,将参数传递给 TurnOn 方法。

我不明白的是,在第一种情况下,为什么我们的更改会导致 Button 类发生更改?Lamp 的公共接口没有改变,那么为什么 Button 需要改变呢?

在第二种情况下,我可以看到这需要我们更改 Button。但在这种情况下,依赖抽象如何改变这一点?当然,如果我有正当理由将接口更改为 Lamp,那么我也会更改 Lamp 和 Button 所依赖的抽象接口。在这种情况下,无论如何我都必须更改 Button,因为它所依赖的抽象已经改变。

我意识到 DIP 还有其他好处,例如更高级别模块的可重用性、更高级别模块对接口的所有权以及在运行时选择依赖项实现的能力,但是我很难理解 DIP 如何减少对依赖项的需求当与较低级别模块的接口发生更改和/或依赖模块中的内部更改可能导致更高级别模块中的更改时,要更改的模块。

4

2 回答 2

1

我相信 DIP 给这个例子带来的重要区别是接口的所有权。特别是哪个层拥有接口,其中 Button 是客户端,Lamp 是服务器。

在对具体类 Lamp 的依赖中,接口 (.TurnOn()) 属于 Lamp 类(服务器)。因此,可以决定仅根据服务器的需要更改 .TurnOn() 方法,因为它拥有该方法,这将需要对 Button 类(客户端)进行后续更改。

当接口抽象为 ISwitchableDevice 接口/抽象类时,所有权将转移到客户端或共享层。因此,对接口的更改不能直接由服务器需求驱动,对 Lamp 类(由服务器拥有)的任何更改都可以在不更改接口的情况下进行。如果需要更改 ISwitchableDevice 接口,那么这将由客户端或共享层的需求驱动。

于 2012-08-22T13:08:02.603 回答
0

例如,关于接口,如果对 Lamp 的构造函数(它是公共接口的一部分)进行了更改,并且您依赖于抽象基础或接口,则这些更改不会传播到按钮的实现(除非你在那里建造它,但这部分是另一个问题)。
至于“纯实现”,一个适当封装的设计,其中没有对任何类型的接口进行更改(构造函数、可以抛出的异常等)并且没有更改公共全局依赖项,那么它不应该影响调用者,因此问题不大。

基本上,关于这些问题,我们正在减少表面积,因此我们依赖较少的细节,尽管一些细节总是必要的。

抽象是无用的混乱还是无价的分离,需要根据具体情况来决定。

于 2012-08-01T09:05:42.253 回答