javaoopsolid-principlesdesign-principlesdependency-inversion

Dependency inversion design choice question


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


Solution

  • 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.