2

Today I was asked a question I could not find an answer for so here I am, asking for your help! The Dependency Inversion Principle states that both concrete classes and abstractions should depend on abstractions right?

Regardless of that we still depend on framework classes like Integer and String. Is there a good answer as to why that is okay?

I know we should not reinvent the wheel just because it may change ever so slightly, and these particular classes I mentioned will most likely never change in a way their users will notice (their interfaces won't change).

jaco0646
  • 15,303
  • 7
  • 59
  • 83
Alvaro Nicoli
  • 21
  • 1
  • 3
  • 4
    Pragmatically speaking, abstractions incur overhead and afford flexibility. You have to find the right tradeoff between the cost and the benefit. If you won't benefit from the increased flexibility (e.g. because you're unlikely to ever substitute the class for anything else), then there's only the overhead left. Experience helps to foresee when the cost outweighs the benefit. – deceze Dec 11 '19 at 16:29

2 Answers2

2

In OOP, an object is the encapsulated combination of data and behavior. The data is hidden; the behavior is exposed. It is these objects to which the DIP applies. Ideally, these objects should be instantiated in a Composition Root, which is the only component that depends on the objects' concrete classes.

Obviously something has to depend on the concrete classes in order to instantiate them. This is typically your DI container. The idea is that the container's sole concern is instantiating concrete classes, so everything else can obey the DIP.

On the other hand, opposite to objects, we have primitive data structures. These classes are not (necessarily) encapsulated, expose their data, and have little or no behavior. It is fine to depend on the concrete classes of data structures. These are not "objects" in the OOP sense. The DIP does not apply to data structures. Dependencies on concrete data structures should be local; however, and not exposed outside of the object that owns those dependencies.

Note that you will often see "hybrids" in code: classes that behave as both objects and data structures. They expose both their data and behavior. Hybrids are the worst of both worlds, and whether you apply the DIP to them or not, the bigger problem is that they are trying to serve two opposing purposes and violating encapsulation.

jaco0646
  • 15,303
  • 7
  • 59
  • 83
2

As an additional point, note that the elements you are citing are actually in java.lang, i.e. there is no import statement, and one could say therefore no dependency to anything induced by using these types, besides the fact you use Java.

As soon as you step out of java.lang, I believe you are usually better off applying DIP, i.e. always prefer using a List<T> over using a ArrayList<T>.

The problem is to limit dependencies across borders of "components" (or module/package...) to only functional dependencies (i.e. abstractions, interfaces in Java). At some point you do need concrete implementations, that are necessarily built using at least some data structures, even if it is only arrays of basic int type. This does not violate DIP.

The use of Plain Old Data as suggested in @jaco0646 's answer, that are not violating DIP is kind of borderline ; in most cases you could use a signature that explicitly passes the fields of the struct you are considering instead of packing them into a single object ; this approach is indeed more general, e.g. you can implement it without having that POD class, maybe relying on some relational DBMS, you can interact with code written in any language etc...

However in practice, it can make sense to use POD in signatures, so that if I add a field to a POD, this will automatically propagate to all signatures that use the struct. Some of these functions may not use the new field, so we are now giving them too much information (we are leaking, with respect to strict "need to know"). Still, it can be a pragmatic answer in many cases to opt for this approach.

If we look at e.g. webservices, there is general tendency to consider POD are not a problem in service signatures, and using them helps keep clients compatible even if some new fields appear in the struct.

Yann TM
  • 1,942
  • 13
  • 22