Dependency inversion principle is implemented using. Dependency Inversion

home / Quarreling

In fact, all principles SOLID are strongly interconnected and their main goal is to help create high-quality, scalable software. But the last principle SOLID really stands out against them. First, let's look at the formulation of this principle. So, dependency inversion principle (Dependency Inversion Principle - DIP): “Dependence on abstractions. There is no dependence on anything specific.. Notorious software development expert, Robert Martin, also highlights the principle DIP and presents it simply as the result of following other principles SOLID— the open/closed principle and the Liskov substitution principle. Recall that the first says that the class should not be modified to make new changes, and the second deals with inheritance and assumes the safe use of derived types of some base type without breaking the correct operation of the program. Robert Martin originally formulated this principle as follows:

one). Upper-level modules should not depend on lower-level modules. Modules at both levels must depend on abstractions.

2). Abstractions should not depend on details. Details should depend on abstractions.

That is, it is necessary to develop classes in terms of abstractions, and not their specific implementations. And if you follow the principles OCP and LSP, then this is exactly what we will achieve. Therefore, let's go back a little to the lesson on. There, as an example, we considered the class bard, which at the very beginning was hardwired to the class Guitar, representing a specific musical instrument:

public class Bard ( private Guitar guitar; public Bard(Guitar guitar) ( this.guitar = guitar; ) public void play() ( guitar.play(); ) )

public class Bard(

private guitar guitar;

public Bard (Guitar guitar )

this. guitar = guitar ;

public void play()

guitar. play();

If we wanted to add support for other musical instruments to this class, then we would somehow have to modify this class. This is a clear violation of the principle OCP. And you may have already noticed that these are also violations of the principle DIP, since in our case our abstraction turned out to be dependent on details. From the point of view of further expansion of our class, this is not good at all. To make our class meet the conditions of the principle OCP we have added an interface to the system instrument, which implemented specific classes representing certain types of musical instruments.

File Instrument.java:

public interface Instrument (void play(); )

public interface Instrument(

voidplay();

File Guitar.java:

class Guitar implements Instrument( @Override public void play() ( System.out.println("Play Guitar!"); ) )

class Guitar implements Instrument(

@Override

public void play()

System. out . println("Play Guitar!");

File lute.java:

public class Lute implements Instrument( @Override public void play() ( System.out.println("Play Lute!"); ) )

public class Lute implements Instrument(

@Override

public void play()

System. out . println("Play Lute!" ) ;

After that we changed the class bard so that, if necessary, we can replace the implementations with exactly those that we need. This brings additional flexibility to the system being created and reduces its cohesion (strong class dependencies on each other).

public class Bard ( private Instrument instrument; public Bard() ( ) public void play() ( instrument.play(); ) public void setInstrument(Instrument instrument) ( this.instrument = instrument; ) )

public class Bard(

private Instrument instrument ;

2 replies

Good point - the word inversion is somewhat surprising (since after applying DIP , the lower-level dependency module apparently doesn't now depend on the higher-level caller module: either the caller or the dependent is now more loosely coupled through an additional abstraction).

You may ask why I use the word "inversion". Frankly, this is because more traditional software development methods such as structured analysis and design tend to create software structures in which high-level modules depend on low-level modules and in which abstractions depend on details. In fact, one of the purposes of these methods is to define a hierarchy of subroutines that describes how high-level modules make calls to low-level modules.... Thus, the dependency structure of a well-designed object-oriented program is "inverted" with respect to the dependency structure, which is usually the result of traditional procedural methods.

One point to note when reading Uncle Bob's paper on DIP is that C++ does not (and at the time of writing, does not) have interfaces, so achieving this abstraction in C++ is usually achieved through abstract/pure virtual base class, whereas in Java or C# the abstraction to loosen the coupling is usually to unbind by abstracting the interface from the dependency and binding the higher level module(s) to the interface.

Edit Just to clarify:

"In some place I also see it's called dependency inversion"

Inversion: Invert dependency management from application to container (like Spring).

Dependency Injection:

Instead of writing a factory pattern, how about injecting the object directly into the client class. So let's let the client class refer to the interface and we should be able to inject the concrete type into the client class. With this, the client class does not need to use the new keyword and is completely separated from the concrete classes.

What about inversion of control (IoC)?

In traditional programming, the flow of business logic is defined by objects that are statically assigned to each other. With inversion of control, flow depends on an object graph that is instantiated by the assembler and made possible by object interactions defined through abstractions. The bundling process is achieved through dependency injection, although some argue that using a service locator also provides inversion of control.

Inversion of control as a design guide serves the following purposes:

  • There is a decoupling of the execution of a particular task from the implementation.
  • Each module can focus on what it is intended for.
  • Modules don't make any assumptions about what other systems do, but rely on their contracts.
  • Replacing modules does not affect other modules.

See for more information.

Last update: 03/11/2016

Dependency Inversion Principle(Dependency Inversion Principle) is used to create loosely coupled entities that are easy to test, modify and update. This principle can be formulated as follows:

Top-level modules should not depend on lower-level modules. Both must depend on abstractions.

Abstractions should not depend on details. Details should depend on abstractions.

To understand the principle, consider the following example:

Class Book ( public string Text ( get; set; ) public ConsolePrinter Printer ( get; set; ) public void Print() ( Printer.Print(Text); ) ) class ConsolePrinter ( public void Print(string text) ( Console.WriteLine (text); ) )

The Book class, which represents a book, uses the ConsolePrinter class to print. When defined like this, the Book class depends on the ConsolePrinter class. Moreover, we have rigidly defined that printing a book is possible only on the console using the ConsolePrinter class. Other options, for example, output to a printer, output to a file, or using some elements of a graphical interface - all this is excluded in this case. The book printing abstraction is not separate from the details of the ConsolePrinter class. All this is a violation of the dependency inversion principle.

Now let's try to bring our classes into line with the dependency inversion principle by separating abstractions from low-level implementation:

Interface IPrinter ( void Print(string text); ) class Book ( public string Text ( get; set; ) public IPrinter Printer ( get; set; ) public Book(IPrinter printer) ( this.Printer = printer; ) public void Print( ) ( Printer.Print(Text); ) ) class ConsolePrinter: IPrinter ( public void Print(string text) ( Console.WriteLine("Print to Console"); ) ) class HtmlPrinter: IPrinter ( public void Print(string text) ( Console.WriteLine("Print to html"); ) )

Now the book printing abstraction is separated from concrete implementations. As a result, both the Book class and the ConsolePrinter class depend on the IPrinter abstraction. In addition, now we can also create additional low-level implementations of the IPrinter abstraction and apply them dynamically in the program:

Book book = new Book(new ConsolePrinter()); book.Print(); book.Printer = new HtmlPrinter(); book.Print();

Dependency inversion is one of the most important programming idioms. There are surprisingly few descriptions of this idiom (principle) on the Russian-language Internet. So I decided to try to make a description. I will do examples in Java, at the moment it’s easier for me, although the principle of dependency inversion is applicable to any programming language.

This description was developed jointly with Vladimir Matveev in preparation for classes with Java students.

Other articles from this series:

Let's start with the definition of "dependency". What is addiction? If your code uses some class internally or explicitly calls a static method of some class or function, this is a dependency. Let me explain with examples:

Below class A inside a method called someMethod() explicitly creates an object of class B and accesses its method someMethodOfB()

Public class A ( void someMethod() ( B b = new B(); b.someMethodOfB(); ) )

Similarly, for example, class B refers explicitly to the static fields and methods of the System class:

Public class B ( void someMethodOfB() ( System.out.println("Hello world"); ) )

In all cases where any class (type A) itself creates any class (type B) or explicitly accesses static fields or class members, this is called straight addiction. Those. important: if a class inside itself works inside itself with another class, this is a dependency. If he also creates this class inside himself, then this straight addiction.

What's wrong with direct dependencies? Direct dependencies are bad because a class that independently creates another class inside itself is "tightly" tied to this class. Those. if it is explicitly written that B = new B(); , then class A will always work with class B and no other class. Or if it says System.out.println("..."); then the class will always output to System.out and nowhere else.

For small classes, dependencies are not terrible. Such code may well work. But in some cases, in order for your class A to work universally in the environment of different classes, it may need other implementations of dependency classes. Those. you will need, for example, not class B , but another class with the same interface, or not System.out , but, for example, output to a logger (for example, log4j).

A direct dependency can be graphically displayed like this:

Those. when you create class A in your code: A a = new A(); in fact, not one class A is created, but a whole hierarchy of dependent classes, an example of which is in the picture above. This hierarchy is “rigid”: without changing the source code of individual classes, none of the classes in the hierarchy can be replaced. Therefore, class A in such an implementation is poorly adaptable to a changing environment. Most likely, it will not be possible to use it in any code, except for the specific one for which you wrote it.

To decouple class A from specific dependencies, apply dependency injection. What is dependency injection? Instead of explicitly creating the desired class in code, dependencies are passed to class A through the constructor:

Public class A ( private final B b; public A(B b) ( this.b = b; ) public void someMethod() ( b.someMethodOfB(); ) )

That. class A now gets its dependency through the constructor. Now, in order to create class A, you will first need to create its dependent class. In this case, it's B:

B b = new B(); A a = new A(b); a.someMethod();

If the same procedure is repeated for all classes, i.e. pass an instance of class D to the constructor of class B , to the constructor of class D — its dependencies E and F , etc., then you will get code, all the dependencies of which are created in the reverse order:

G g = new G(); H h = new H(); F f = new(g,h); E e = new E(); D d = new D(e,f); B b = new B(d); A a = new A(b); a.someMethod();

Graphically, this can be displayed like this:

If you compare 2 pictures - the picture above with direct dependencies and the second picture with dependency injection - you can see that the direction of the arrows has changed to the opposite. For this reason, the idiom is called "dependency inversion". In other words, dependency inversion lies in the fact that the class does not create dependencies on its own, but receives them in the created form in the constructor (or otherwise).

Why is dependency inversion good? With dependency inversion, you can replace all dependencies in a class without changing its code. And this means that your class A can be flexibly configured for use in another program than the one for which it was originally written. That. The principle of dependency inversion (sometimes called the principle of dependency injection) is key to building flexible, modular, reusable code.

The disadvantage of dependency injection is also visible at first glance - objects of classes designed using this pattern are laborious to construct. Therefore, dependency injection (inversion) is usually used in conjunction with some library designed to facilitate this task. For example, one of the Google Guice libraries. Cm. .

© 2022 skudelnica.ru -- Love, betrayal, psychology, divorce, feelings, quarrels