hammerfest
hammerfest

Reputation: 2273

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

Upvotes: 1

Views: 563

Answers (2)

plalx
plalx

Reputation: 43718

"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"

While it's indeed not DIP, the explanation is wrong IMO. The problem is that the author failed to recognize the distinction between a class/component and a layer/package/module. DIP is a principle that's only applicable to relationships between modules, which obviously is not applicable when there's a single module.

As for the point #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.

We need to be very careful here because "ownership" goes beyond "being in the same package".

e.g. this would be a DIP violation

enter image description here

Upvotes: 1

jaco0646
jaco0646

Reputation: 17066

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.

Upvotes: 2

Related Questions