I am currently reading an otherwise excellent tutorial regarding the Dependency Inversion Principle
https://www.baeldung.com/java-dependency-inversion-principle
and there is something I am not able to interpret despite considerable long time of thinking
The relevant part from definition of DIP: "High-level modules should not depend on low-level modules. Both should depend on abstractions."
In point 3.1 "Design Choices and the DIP" the author introduces the principle through an example where a StringProcessor
class uses a StringReader
and a StringWriter
component and gives multiple design choices wrt using interfaces/classes and packages. My problem is with choice 2 which is
"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"
StringProcessor
is the "high level component" which depends on the "abstractions" i.e. StringReader
and StringWriter
interfaces, thereby fulfilling DIP definition from one side, that is clear. Now given the terminology used throughout the article "the implementations" referred in the first sentence, e.g. a ConcreteStringReader
and a ConcreteStringWriter
class would be the "low level components" here and what I am just not able to understand how they do not depend on the "abstractions" i.e. on the interfaces they implement, regardless of the containing packages
Obviously putting implementations in the same package with their interfaces might not be the best from code organization point of view, but how this violates the verbatim DIP definition above wrt depending on abstractions is currently beyond my understanding
Perhaps someone with a deeper theoretical knowledge on the topic might help me out here
The general concept implied is that one package equates to one level of abstraction. Therefore, in section 3.1.2, the concrete implementations "own" their abstractions by virtue of being in the same package; because anywhere the package is published, those implementations are along for the ride. The coupling among classes that share a package manifests to some extent in syntax, even in Java 8. For example, import
statements aren't necessary and classes & methods with the default access modifier are visible.
Still, it's easier to see the flaw in section 3.1.2 in light of JPMS features. Modules are defined at the package level, formalizing the concept that a package is a single level of abstraction. In terms of the DIP then, dependencies are also considered at the package level. If a package contains concrete implementations, it is considered low level, and should not have incoming dependencies.
An entire book that dives deep into this topic is Java Application Architecture: Modularity Patterns.