A common problem that you'll need to address when designing your systems is dependency. There are several questions that will arise when it comes to this topic such as, where are my points of dependency? And to what degree is my system, or subsystems dependent on a particular resource? Software dependency is essentially coupling which defines how much reliance there is between different components of your software. If a part of your system is highly coupled, then the degree to which they rely on one another is considered high, whereas low dependency means that your system has a lower degree of coupling.For a review on coupling, you can check out the lesson on cohesion uncoupling from course one. Dependency is an important topic because it will determine how easily you can make changes to your system. Think about it this way, if you were dependent on a specific type of food for survival, then you wouldn't be able to substitute it for something else. In a real life example, our bodies are dependent on water for survival. We cannot replace water with bread and hope to get the same results as if we were drinking water. You'll end up facing the same issue in software. Trying to substitute one class or resource for something else is not easily done, or sometimes even possible. This is because your system is dependent on a specific function that may only be offered by that particular class. To address this issue, we use the dependency inversion principle which will help you make your systems more robust and flexible. The principle states that high level modules should depend on high level generalizations and not on low level details. This means that your client classes should depend on an interface or abstract class instead of referring to concrete resources, and that your concrete resources should have their behaviors generalized into an interface or abstract class. The idea is that interfaces and abstract classes are considered high level resources, while a concrete class is considered a low level resource. An interface or abstract class defines a general set of behaviors, and the concrete classes provide the implementation for these behaviors. This way your client classes can be independent of low level functionality.All the design patterns that we've introduced are built on this principle. Let's take a look at the difference between low level dependency and high level dependency, to demonstrate how the dependency inversion principle works. In a conventional software design, your system architecture might look like this. Your subsystems are directly dependent on each other which means that your client class will directly reference some concrete class in your enterprise subsystem which will directly reference some concrete class in your backend. This form of dependency is called a low level dependency, because the client classes are naming and making direct references to a concrete class. But, what happens when you need to change this reference in the future? Here, you have direct naming and referencing to the concrete class called quicksorting which you use to sort of list sent to your system by a user. The issue is that if you were to implement a different sorting class called mergesort, you would need to make significant changes to your client subsystem class. Notice that you've changed the type of sorting class from quick sorting to mergesorting, and you've also had to change the sorting method call. Imagine how much work you would need to do every time you want to make a sorting algorithm changed your system. Not only is it impractical, these kinds of changes could have unexpected side effects if you missed any old references. The dependency inversion principle addresses this issue by generalizing low level functionality into interfaces or abstract classes, so that when alternative implementations are offered, they can be used with ease. When you use the dependency inversion principle, the overall architecture of your system will look very similar to the design patterns that we've explored. You need to generalize the behaviors across each subsystem into an interface. The concrete classes of each subsystem will then implement the interface. And finally, your client classes will make references to the interface rather than directly to the concrete classes. As you can see, instead of declaring a concrete class, you can declare the sorting interface. Then you can determine which instance of sorting class you want the client subsystem to have during instantiation. Furthermore, since you can generalize the sorting behavior into a method called sort in the interface, you won't need to change the method call in the client subsystem. Since your client class is dependent on a high level generalization rather than a low level concrete class, you can easily change which resource your client will be using. The generalization gives you a degree of indirection by allowing you to invoke behaviors in a concrete class through an interface. High level dependency will prevent your client class to be directly coupled with a specific concrete class. This means that a client class is dependent on expected behaviors, not on a specific implementation of behaviors. Unlike in procedural languages, such as C, where calls are often made directly during a point in a program's execution, object oriented systems don't always need to make method calls so directly. In fact, method calls in an object oriented design should be done using a level of indirection. The dependency inversion principle suggests that you should make use of indirections by writing dependencies to generalizations rather than directly to a concrete class. Just like with the design patterns we've looked at in this course, you want to use the dependency inversion principle during the design process. Since building a flexible, reusable and maintainable system will prolong the life of your software, applying this design principle will help you achieve those goals. To summarize, the dependency inversion principle is a means to change the referencing of concrete classes from being direct to indirect, generalize the behaviors of your concrete classes into abstract classes and interfaces, have client classes interact with your system through a generalization rather than directly with concrete resources, and put emphasis on high level dependency over low level concrete dependency. Despite the extra work that may be required to design and implement your system, the amount of time and resources that you gain further down the road is worth the effort spent. Large systems are difficult to maintain and when your system becomes too heavily coupled changes will be hard to make. The dependency inversion principle will help you with insulating parts of your system from specific implementations of functionality. Just like with design patterns, you want your program to do generalizations, like interfaces and abstract classes, rather than directly to a concrete class. It's a principle that you should follow in order to create a robust software solution.