DEV Community

vinaykumar0339
vinaykumar0339

Posted on

#5 Dependency Inversion Principle ['D' in SOLID]

DIP - Dependency Inversion principle
The Dependency Inversion Principle is the fifth principle in the Solid Design Principles.

  1. High-Level Modules should not depend on Low-Level Modules. Both Should depend on abstraction.
  2. Abstraction should not depend on details. Details should depend on Abstraction.

High-Level Modules.

  1. The High-Level Module contains important policy decisions and business logic.
  2. They Should not be affected by changes in Low-Level Modules.

Low-Level Modules.

  1. Low-Level Modules contains implementation details.
  2. Changes in Low-Level Modules should not affect High-Level Modules.

Abstractions

  1. Use Interfaces or Abstraction class to create an abstraction Layer.
  2. High-level modules depend on this abstraction, allowing low-level module changes without affecting high-level logic.

Violating DIP:

class FileLogger {
    func log(message: String) {
        print("Loggin Message to file: \(message)")
    }
}

class UserService {
    private let logger = FileLogger()

    func createUser(name: String) {
        logger.log(message: "User \(name) Created.")
    }
}

// usage
let userService = UserService()
userService.createUser(name: "Vinay Kumar")
Enter fullscreen mode Exit fullscreen mode

Issues with Violating DIP:

  1. Tight Coupling:
    • UserService is tightly coupled to FileLogger, making it difficult to change the logging mechanism without modifying UserService.
  2. Reduced Flexibility:
    • Switching to a different logging method (e.g., database logger, cloud logger) requires changing UserService.
  3. Hard to Test:
    • Testing UserService requires an actual FileLogger, making it harder to isolate and test.

Adhering to DIP:
Using Protocol We can adhere to DIP.

protocol Logger {
    func log(message: String)
}

class FileLoggerDIP: Logger {
    func log(message: String) {
        print("Logging message to file: \(message)")
    }
}

class ConsoleLogger: Logger {
    func log(message: String) {
        print("Logging message to console: \(message)")
    }
}

class UserServiceDIP {
    let logger: Logger

    init(logger: Logger) {
        self.logger = logger
    }

    func createUser(name: String) {
        self.logger.log(message: "User \(name) Created")
    }
}

// usage
let fileLoggerDIP = FileLoggerDIP()
let consoleLogger = ConsoleLogger()
let userServiceDIPUsingFileLogger = UserServiceDIP(logger: fileLoggerDIP)
let userServiceDIPUsingConsoleLogger = UserServiceDIP(logger: consoleLogger)

userServiceDIPUsingFileLogger.createUser(name: "Vinay Kumar DIP FileLogger")
userServiceDIPUsingConsoleLogger.createUser(name: "Vinay Kumar DIP ConsoleLogger")
Enter fullscreen mode Exit fullscreen mode

Benefits of Adhering to DIP:

  1. Improved Maintainability:
    • Changes in low-level modules (like FileLogger) do not affect high-level modules (like UserService).
  2. Enhanced Flexibility:
    • Easily switch between different logging mechanisms without changing the high-level module.
  3. Increased Testability:
    • High-level modules can be tested with mock implementations of abstractions.

Drawbacks of Adhering to DIP:

  1. Increased Complexity:
    • Requires creating additional interfaces or abstract classes.
  2. More Boilerplate Code:
    • More code is needed to set up the abstractions.

Mitigating Drawbacks:

  1. Balanced Approach:
    • Apply DIP where it provides significant benefits, balancing simplicity and extensibility.
  2. Clear Documentation:
    • Maintain clear documentation to help developers understand the dependencies.
  3. Use of Dependency Injection Frameworks:
    • Use frameworks to manage dependencies efficiently.

Conclusion:
You can create more maintainable, understandable, and flexible software by thoughtfully understanding and applying the Dependency Inversion Principle. Ensuring that high-level modules depend on abstractions rather than low-level details promotes better software design and enhances the overall quality of the codebase.

Interface Segregation Principle
Single Responsibility Principle
Check My GitHub Swift Playground Repo.

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay