开闭原理(OCP)与依赖反转原理(DIP)


Answers:


16

我之前写过关于开放式封闭原则(OCP)与Liskov的替代原则(LSP)的答案,这两个原则相互关联很多,但在概念上仍然有所不同,有些人为设计的例子中有一个但没有另一个。由于这个答案,我将只简要介绍一下OCP,然后深入探讨DIP,以及使该滴答作响的原因。

让我们先通过首先解释不同的原理,来讨论OCP与依赖反转原理(DIP)的关系和区别。

依赖倒置原则

阅读Bob叔叔的OOD原理,您会发现DIP声明以下内容:

取决于抽象,而不取决于混凝土。

Java中的抽象可以简单地通过interfaceabstract关键字来实现,这意味着您对代码必须遵循的某些软件实体具有“合同”。某些编程语言没有工具来明确设置要遵循的代码的行为,因此必须以更加手动的方式遵循抽象,而不是让编译器帮助您执行合同。例如,在C ++中,您具有使用虚方法的类,而动态编程语言(例如Javascript)则必须确保以相同的方式使用对象(尽管在Javascript中,TypeScript中对此进行了扩展,添加了类型系统来帮助您)编写经编译器验证的合同)。

该名称包含“反转”一词,因为传统上(您知道在编程的黑暗时代中)您编写的软件结构具有较高级别的模块,具体取决于较低级别的模块。例如,ButtonAtKitchen对于KitchenLamp1和具有处理输入是有意义的KitchenLamp2。不幸的是,这使得软件比需要的更加具体,对象图如下所示:

ButtonAtKitchen处理KitchenLamp1和KitchenLamp2

因此,当您使软件更加通用时,可以添加“合同”。注意对象图中的箭头如何“反转”方向。那个厨房灯现在依赖于Button。换句话说,细节现在依赖于抽象,而不是相反。

KitchenButton现在具有IButton抽象,厨房灯具依赖于此

因此,我们对DIP有更一般的定义,在Bob叔叔的DIP原始文章中也有详细介绍。

答:高级模块不应依赖于低级模块。两者都应依赖抽象。B.抽象不应依赖细节。细节应取决于抽象。

开闭原则

继续遵循Bob叔叔的原则,您会发现OCP声明以下内容:

您应该能够扩展类的行为,而无需对其进行修改。

实现此目标的一个示例是采用策略模式,其中关闭一个Context类以进行修改(即,您根本无法更改其内部代码),但也可以通过其协作依赖性(例如,策略类)对其进行扩展

从更一般的意义上讲,任何模块都可以通过其扩展点进行扩展。

带有扩展点的模块

OCP类似于DIP,对不对?

,不是。

尽管他们都在讨论抽象,但是它们在概念上有所不同。两种原则都针对不同的上下文,OCP在一个特定模块上,DIP在多个模块上。您可以同时使用大多数“四人行”设计模式同时实现这两个目标,但是仍然可以避开这条路。

在上面提到的DIP示例中,对于按钮和厨房灯,没有一个厨房灯是可扩展的(目前也没有任何要求说明它们是必需的)。该设计打破了OCP,但遵循了DIP

一个相反的(也是人为的)示例是厨房灯是可扩展的(扩展点类似于LampShade),但是按钮仍取决于灯。它正在打破DIP,但遵循OCP

别担心,它会发生

实际上,这是您经常会在生产代码中看到的东西,它的某些部分可能会违反原则。在较大的软件系统中(即,比上面的示例大的任何东西),您可能会违反一个原则,但通常会保留另一个原则,因为您需要保持代码简单。在我看来,这对于小型且独立的模块来说是可以的,因为它们与“单一责任原则”(SRP)有关。

一旦某个模块变得复杂,尽管您很可能需要牢记所有原则,然后将其重新设计或重构为某种众所周知的模式。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.