8

I was reading SOLID principles on a website, in which for D - Dependency Inversion Principle it says:

“Depend on abstractions, not on concretions”

In other words. we should design our software in such a way that various modules can be separated from each other using an abstract layer to bind them together.

Dependency Inversion Principle Example

The classical use of this principle of bean configuration in the Spring framework.

In spring framework, all modules are provided as separate components which can work together by simply injected dependencies in other module. This dependency is managed externally in XML files.

These separate components are so well closed in their boundaries that we can use them in other software modules apart from spring with same ease. This has been achieved by dependency inversion and open closed principles. All modules expose the only abstraction which is useful in extending the functionality or plug-in in another module.

I have a few questions on it.

  • Depend on abstractions, not on concretions It means, coding to interfaces, not to concrete classes?
  • Why Spring bean example is given? Yes we can use interfaces to inject the dependency. But, we can also inject a concrete class?
8

Depend on abstractions, not on concretions It means, coding to interfaces, not to concrete classes?

For languages that support interfaces, this is generally the case. But that abstraction layer can be provided via other means, such as an abstract class, a factory, reflection etc. The important thing is that you do not directly couple a requirement to a named, concrete, class.

*Why Spring bean example is given? Yes we can use interfaces to inject the dependency. But, we can also inject a concrete class?

Presumably, because you are looking at "SOLID Principles in Java" (emphasis mine). Spring is a popular DI framework for Java. "But, we can also inject a concrete class?" You always inject a concrete class. But which concrete class is determined by your spring configuration. The receiver of the injection only depends on an abstraction, not that specific class. Having something else tell it what concrete implementation it is to use is the essence of DI.

  • 2
    Actually being bound to a named concrete class is only a problem if the client has a way to know if it's concrete. Some languages make hiding this trivial. Others have naming conventions or require a recompile that force clients to know. If you don't know that it's concrete it's already an abstraction. – candied_orange Nov 28 '19 at 22:05
6

Depend on abstractions, not on concretions.
It means, coding to interfaces, not to concrete classes?

Also known as "coding to the interface (not to confuse with interface the keyword), not the implementation".
It is an ancient technique called abstraction. Any layer of abstraction tries to hide irrelevant details and thus reduce complexity, allowing bigger and less error-prone systems to be built. The cost is that both the way to hide them, as well as not taking advantage of the details themselves, will often have a cost.

It doesn't have anything to do with classes, functions, constants, or whatever. It simply means only depending on the contract, the promise which the implementation is bound to honor on pain of being called defective, instead of going with "works for me" (now, here, while noone looks).

Why Spring bean example is given? Yes we can use interfaces to inject the dependency. But, we can also inject a concrete class?

The Dependency Inversion Principle (DIP) heavily depends on it, as it makes substituting different implementations easy, even expected.

The reason a prominent Java framework is used is simply that your source works with Java.

Whether you inject a class or an interface doesn't make any difference to DIP, but interfaces are the backdoor to MI in Java, and contain minimal hard-coded behaviour and no data.

1

Abstraction does not necessarily mean an interface. If you think about two interacting components, as the code evolves there will be things that change, but there will be aspects that stay more or less the same. But changes in one component generally cause changes in the other, dependent component.

If you recognize those aspects that don't change, or that form the essential features of the interaction, you "extract" them into an abstraction - a thing that is an expression of those aspects: could be an interface, a concrete or abstract base class that can be inherited, a data structure, a set of collaborating classes, etc. But, it's not about just adding an interface in between - that's just extra code; the interface needs to be a well-designed generalization that's expressive enough to cover most of the cases without it being changed (too frequently - until it stabilizes). So it's not necessarily easy to get right.

Once you've done that, you then rewrite both components in terms of that abstraction (what you called "coding to interfaces") - typically, one side makes calls through the abstraction, the other provides a service behind the abstraction, making sure to adhere to the "contract" defined by the abstraction.

In your own code, if you have a component A depending on B, you may need to wait for several different variants of B to emerge before you find what's common to all of them, and pull that out into an interface, or some other kind of an abstraction. Or you may know enough about the problem domain to be able to guess what a good abstraction would look like.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.