Design Patterns
Dependency Inversion Principle

Dependency Inversion Principle

The Dependency Inversion Principle is a design principle that states that high-level modules should not depend on low-level modules, but rather both should depend on abstractions. This means that the design of a system should depend on abstractions, rather than on concrete implementations.

To follow the Dependency Inversion Principle in your design:

  1. Identify the high-level and low-level modules in your system. The high-level modules are typically the ones that provide the overall functionality of the system, while the low-level modules are the ones that provide the specific implementation details.
  2. Create abstractions for low-level modules.
  3. Have high-level modules depend on abstractions.
  4. Have low-level modules implement abstractions.

Here's an example that violates the Dependency Inversion Principle in Python:

This example violates the Dependency Inversion Principle because the BackupService class has a direct dependency on the FileSystem class. This means that any changes to the FileSystem class could potentially affect the BackupService class, and vice versa. This violates the principle, as it means that the high-level BackupService class depends on the low-level FileSystem class.

To fix this issue and follow the Dependency Inversion Principle, we can introduce an abstraction for the file system functionality. Here's the refactored implementation:

In this revised version of the system, the BackupService class depends on the IFileSystem interface rather than on the FileSystem class directly. This follows the Dependency Inversion Principle, as it ensures that the high-level BackupService class does not depend on the low-level FileSystem class.

By introducing the abstraction, we have decoupled the BackupService class from the FileSystem class, making the system more flexible and easier to modify. If we need to change the way that the system reads and writes files, we can do so by modifying the FileSystem class without affecting the BackupService class. This allows us to add new features or make changes to the system without causing unintended consequences.

However, this example still uses an object of the FileSystem class. Is this not a violation of the Dependency Inversion Principle?

No, using an object of the FileSystem class in the BackupService class does not necessarily violate the Dependency Inversion Principle. The key principle here is that the high-level module (in this case, the BackupService class) should depend on abstractions, rather than on concrete implementations.

In this example, the BackupService class is still using an object of the FileSystem class. However, the BackupService class is not directly depending on the FileSystem class. Instead, it is depending on the IFileSystem interface, which is implemented by the FileSystem class.

This means that the BackupService class is not tightly coupled to the FileSystem class. If we need to change the way that the system reads and writes files, we can do so by creating a new class that implements the IFileSystem interface and using that class instead of the FileSystem class. This allows us to make changes to the low-level FileSystem class without affecting the high-level BackupService class.