Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Dependency Inversion

00:00 In the previous lesson, I described the interface segregation principle. In this lesson, I cover the last part of SOLID, the dependency inversion principle.

00:10 I find this one the hardest to explain. It states abstractions should not depend upon details. Details should depend on abstractions. This typically means that there should be some abstraction layer between high-level and low-level models.

00:27 You don’t want high-level models importing directly from low-level models, as it binds them together too tightly. Let’s see if an example can make this clearer.

00:40 The difference with this one can be a little subtle. Consider a program that has a front-end interface and a back end that encapsulates talking to a database. You have a class for the front end, which uses dependency injection to handle the backend.

00:56 When the front end goes to display data, it uses the back end’s .get_data_from_database() method. Likewise, there’s a back-end class which then implements that method.

01:08 The challenge here is the back end is the wrong abstraction. What you really want to do is abstract the source of the data. If I want to support multiple places to get data from, I’m going to end up having to violate the open-closed principle and change the way the back end works. To fix this problem, you need one more layer of indirection.

01:35 This time around, there are a few more classes. The front end still uses injection, but instead of injecting a back end, which will always be the same, it injects a data source, and sources of data can be different.

01:49 All data sources implement the same .get_data() method, which the front end uses to fetch content.

01:57 The back end has been replaced by three classes: an abstract base class that declares the .get_data() interface, and to prove my point, I’ve provided two different data sources, one for the database and one for an API, both of which implement .get_data().

02:15 By inserting the extra layer of abstraction here, you do away with the open-closed violation. You’ve moved where the dependency is, putting it on its head, thus inverting it, which is where the name comes from.

02:30 Dependency injection helps address this principle, but as you saw, you can use dependency injection and inject the wrong thing, still leaving you with the problem.

02:40 I actually don’t feel like the example code was Pythonic enough. As far as I’m concerned, the DataSource class isn’t really necessary. I’d be just as comfortable documenting a data source protocol stating that any class that implements .get_data() can be a source. That would feel more Pythonic to me, but it hides away the abstraction in convention, making the example less clear, hence why I stuck with an ABC to highlight the difference between the bad and better versions.

03:07 If I were actually building this, I’d probably just stick with duck typing a protocol and be done with it.

03:14 Wow, that’s more principles than a school board convention. Well, assuming your school board conventions have less than five principals. Last up, I’ll summarize this course, as well as point you at sources of further investigation for all three parts of the course.

Become a Member to join the conversation.