DIP 指出:
- 高级模块不应该依赖于低级模块。两者都应该依赖于抽象。
- 抽象不应依赖于细节。细节应该取决于抽象。
OCP 规定:
软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。
我认为如果我们满足 DIP,它也会涵盖 OCP,那么,为什么我们将这两个原则分开?
DIP 指出:
- 高级模块不应该依赖于低级模块。两者都应该依赖于抽象。
- 抽象不应依赖于细节。细节应该取决于抽象。
OCP 规定:
软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。
我认为如果我们满足 DIP,它也会涵盖 OCP,那么,为什么我们将这两个原则分开?
我认为遵守 DIP 更容易遵守 OCP。但是,一个不能保证另一个。
例如,我可以创建一个类,该类的方法采用base
. 如果base
是一个抽象类,那么我将遵守 DIP,因为我已经反转了对调用者的依赖。但是,如果该方法中的代码执行以下操作:
if (base is derived)
(derived)base.DoSomethingSpecificToDerived;
elsif (base is evenMoreDerived)
(evenMoreDerived)base.DoSomethingSpecificToEvenMoreDerived;
然后它不符合 OCP,因为我每次添加新衍生产品时都必须对其进行修改。
这是一个非常人为的例子,但你明白我的意思。
Bob Martin 叔叔将开闭原则 (OCP) 和依赖倒置原则 (DIP) 作为 SOLID 原则中的两个推广开来,他自己表示 DIP 源于 OCP 和 Liskov 替换原则的应用:
在本专栏中,我们将讨论 OCP 和 LSP 的结构含义。严格使用这些原则所产生的结构本身可以概括为一个原则。我称之为“依赖倒置原则”(DIP)。
Robert C. Martin,工程笔记本,C++ 报告,1996 年。
所以你说得对,每个 DIP 实例都是 OCP 的一个实例,但 OCP 更通用。这是我最近遇到的 OCP 但不是 DIP 的用例。许多 web 框架都有信号的概念,在一个动作中,一个信号被触发。发送信号的对象完全不知道注册了信号的听众。每次你想为信号添加更多的监听器时,你可以在不修改发送者的情况下这样做。
这显然是 OCP(“禁止修改,开放扩展”)的例证,但不是 DIP,因为发送者不依赖于任何东西,所以谈论它是否依赖于更抽象或更抽象的东西是没有意义的。
更一般地说,您可以说观察者模式(GoF 模式之一)描述了如何遵守 OCP 而不是 DIP。阅读 GoF 的书,看看哪些与 OCP 相关,以及其中有多少与 DIP 无关,这会很有趣。
DIP 告诉您如何组织依赖关系。它不会告诉您何时完成特定界面。
粗略地说,OCP 的信息是拥有完整但简约的界面。换句话说,它会告诉您何时完成界面,但不会告诉您如何实现这一点。
在某种意义上,DIP 和 OCP 是正交的。
那么,为什么我们要把这两个原则分开呢?
至于设计模式和命名原则,它们几乎都有一个共同点:
找出变化并封装(隐藏)它。
更喜欢聚合而不是继承。
设计到接口。
即使命名的模式和原则在某种意义上部分重叠,它们也会告诉你比上述三个一般原则更具体的东西(在更具体的情况下)。
@CS的好答案。总结一下,
public abstract class MyClass {
DependencyOne d1;
DependencyTwo d2;
MyClass() {
d1 = new DependencyOne();
d2 = new DependencyTwo();
}
}
OCP 是满意的,因为我们可以扩展类。违反了 DIP,因为我们直接实例化了依赖项。
现在的挑战是,我们能否想到一个符合 DIP 的 OCP 违规行为。我能想到的最好的例子是注释。在Java中,我们使用@Deprecated
注解来标记可以修改的代码,从而违反了OCP。同时,这段代码在抽象和依赖方面可能完全符合 DIP。某些库使用@Beta
注释来达到类似的效果。
除了没有依赖关系的类的无效示例之外,我无法想象一个符合 DIP 且无法扩展的示例,这不是很有趣。我会说 DIP 意味着对扩展的开放性。但是,可能存在 DIP 并不意味着封闭性修改的边缘情况。
OCP 使依赖类易于使用。OCP 通过将旧实现与新版本分离来实现接口的异步使用。它允许依赖它的事物继续依赖它,即使面对其他目的的变化。这样一来,一个班级就不必关心是谁在给它打电话。
DIP 做了几件事。它使依赖外部类变得容易。依赖注入通过鼓励将创建职责与消费分离来实现依赖关系的替换。该模式没有创建要使用的外部依赖项,而是声明它应该由外部提供。最终,这鼓励了幂等的代码(不改变外部状态的代码)。幂等代码很好,因为可以验证它只执行立即可见的操作。它没有外部副作用。它非常可测试、易于理解和可读。