依赖倒置设计选择题

Dependency inversion design choice question

我目前正在阅读关于依赖倒置原则的优秀教程

https://www.baeldung.com/java-dependency-inversion-principle

尽管思考了相当长的时间,但有些事情我还是无法解释

DIP 定义中的相关部分:“高级模块不应该依赖于低级模块。两者都应该依赖于抽象。”

在第3.1点“Design Choices and the DIP”作者通过一个例子介绍了其中的原理StringProcessor class使用了StringReaderStringWriter 组件,并使用 interfaces/classes 和包提供多种设计选择。我的问题是选择 2

"StringReaderStringWriter 是与实现一起放在同一个包中的接口。StringProcessor 现在依赖于抽象,但是低级组件不要。我们还没有实现依赖倒置

StringProcessor“高级组件”,它依赖于 “抽象”StringReaderStringWriter接口,从而从一侧完成DIP定义,很清楚。现在给出在第一句中提到的整篇文章“实现”中使用的术语,例如ConcreteStringReaderConcreteStringWriter class 将是 “低级组件” 这里我只是无法理解他们为什么不取决于 “抽象” 即它们实现的接口,而不管包含的包

显然,从代码组织的角度来看,将实现与它们的接口放在同一个包中可能不是最好的,但是这如何违反上面逐字的 DIP 定义取决于抽象目前超出我的理解

也许对该主题具有更深入理论知识的人可以帮助我

隐含的一般概念是一个包等同于一个抽象级别。因此,在 3.1.2 节中,具体实现通过在同一个包中“拥有”它们的抽象;因为无论包发布到哪里,这些实现都会随之而来。共享包的 类 之间的耦合在某种程度上体现在语法上,即使在 Java 8 中也是如此。例如,import 语句不是必需的,类 & 方法与默认访问修饰符可见。

不过,根据 JPMS 的特性,更容易看出第 3.1.2 节中的缺陷。模块是在包级别定义的,形式化了包是单一抽象级别的概念。那么就 DIP 而言,依赖关系也在包级别考虑。如果一个包包含具体的实现,它被认为是低级别,并且不应该有传入的依赖项。

深入探讨该主题的整本书是 Java Application Architecture: Modularity Patterns

"StringReader and StringWriter are interfaces placed in the same package along with the implementations. StringProcessor now depends on abstractions, but the low-level components don't. We have not achieved inversion of dependencies yet"

虽然确实不是DIP,但IMO的解释是错误的。问题是作者没能区分class/component和layer/package/module。 DIP是一个只适用于模块之间关系的原则,在单个模块的情况下显然不适用。

至于第 4 点,

Likewise, item 4 is a more decoupled DIP implementation. In this variant of the pattern, neither the high-level component nor the low-level ones have the ownership of the abstractions.

我们在这里需要非常小心,因为“所有权”超越了“在同一个包中”。

例如这将违反 DIP