From the course: Learning SOLID Programming Principles

Introduction to the Dependency Inversion Principle - Python Tutorial

From the course: Learning SOLID Programming Principles

Introduction to the Dependency Inversion Principle

- [Narrator] The fourth solid design principle I'd like to look at is the dependency inversion principle. The dependency inversion principle has two elements. First, high-level modules should not depend on low-level modules, both should depend on abstractions. Second, abstractions should not depend on details, details should depend on abstractions. There's a common theme to these two elements, depend on abstractions. The inversion part of this principle makes a distinction between two points of view. First, the direct dependency where a concrete class names another concrete class in the code, and the inverted dependency where a concrete class depends only on an abstract interface, the runtime class that implements the interface will be supplied later. In Python, direct dependency on a class name often shows up in object construction. The name of the class is the dependency. The other place it can show up is in type annotations. For example, this class sample list which extends list bracket sample bracket has a direct dependency between the sample list class and the sample class. It also depends on the list class. It's difficult to imagine an abstraction that summarizes the sample class. I'm a fan of applying the dependency inversion principle widely but not making things needlessly complex. Because of duck typing, the idea of abstraction in general isn't profoundly relevant in Python. All that really matters in Python is the method names and the attributes. This leads us to two approaches to defining abstract classes in Python. One choice is to use the ABC module to create abstract classes. These would use the ABC metaclass as their metaclass. Using ABC as the abstract superclass lets us define abstract methods that must be implemented. Another choice is to rely on duck typing, and a third choice is to rely on mixin definitions. Here's the example of using the ABC module to create an abstract class definition. This class for analysis is abstract, it depends on the ABC superclass. A concrete subclass of this must be created to be used at runtime. In particular that concrete class needs to implement the report method because in this definition it's marked as abstract. Another common choice is duck typing. This can be formalized by using the typing protocol type to define an annotation that will encompass the concrete implementations. In this case, the type annotation on individual methods need to be matched by a tool like mypy. There's no other formal relationship among the classes. There's no explicit superclass for a class that implements a protocol. A class like reader here can reference the protocol. A tool like mypy will compare the given class to the protocol to confirm that duck typing will work out correctly at runtime. Using Python mixin class definitions is a third interesting way to provide a dependency injection. This isn't entirely a runtime injection of a class, but it does allow a great deal of flexibility. The methods are defined in a way that apply to a variety of kinds of collections. The sample list class combines the concrete list of sample class with the abstract stats method class injecting those new methods into the list class. Code that depends on concrete classes can be difficult to change. Any extension which creates a new concrete class can cause ripples in the form of rewrites to all the software that depends on the old concrete class. When we depend on abstractions, a new concrete implementation will be less disrupted.

Contents