Dependency Injection & Dependency Inversion Principle
Dependency Injection & DIP

Dependency Injection & Dependency Inversion Principle

Dependency injection is a design pattern where the dependencies of a class are provided from the outside rather than being created internally. This makes the class more modular, testable, and easier to maintain.

Let's see below example:

class UserRepository {
    func getUser() -> String {
        return "Rishabh"
    }
}

class UserService {
    let userRepository = UserRepository()
    
    func greetUser() {
        userRepository.getUser()
    }
}

// Usage
let userService = UserService()
userService.greetUser() // Output: Hello, Rishabh!        

We can see that UserService class is creating an instance of UserRepository. This also means that UserService class is tightly coupled with UserRepository.

This violated the SOLID principle and restricted us from using the same for creating the test cases.

Here are two ways to remove this tight coupling.

  1. Pass an instance of UserRepository in the init method of UserService.
  2. By using the protocol: We can extend this solution to work with different user repositories as app scenarios grow like UserRepository, ClientRepository, etc.

Pass an instance of UserRepository in the init method of UserService.

class UserRepository {
    func getUser() -> String {
        return "Rishabh"
    }
}

class UserService {
    let userRepository: UserRepository
    
    init(userRepository: UserRepository) {
        self.userRepository = userRepository
    }
    
    func greetUser() {
        userRepository.getUser()
    }
}


// Usage
let userRepository = UserRepository()
let userService = UserService(userRepository: userRepository)
userService.greetUser() // Output: Hello, Rishabh!        

Here, We have resolved the dependency injection principle but this violates the Dependency Inversion Principle (DIP) of the SOLID principles.

The Dependency Inversion Principle states that high-level modules should not depend on low-level modules. Both should depend on abstractions. Additionally, abstractions should not depend on details. Details should depend on abstractions.

The UserService class depends directly on the UserRepository class, which is a concrete implementation. To adhere to the Dependency Inversion Principle, UserService should depends on an abstraction (an interface or protocol) rather than a concrete class.

Here's how we can refactor the code to comply with the Dependency Inversion Principle:

// Define protocol
protocol UserRepository {
    func getUser() -> String
}

// Conclere class that confirm to UserRepository protocol
class ConcreteUserRepository:UserRepository {
    func getUser() -> String {
          return "Rishabh "
      }
}

// class that accept UserRepository in init & do the method class
class UserService {
    let userRepository: UserRepository
    
    init(userRepository: UserRepository) {
          self.userRepository = userRepository
      }
    
    func greetUser() {
        userRepository.getUser()
    }
}

// Usage
let userRepository = ConcreteUserRepository()
let userService = UserService(userRepository: userRepository)
userService.greetUser() // Output: Hello, Rishabh yadav1!        

  1. Define a protocol (or interface) that abstracts the UserRepository:
  2. Implement the protocol in a concrete class called ConcreteUserRepository.
  3. Update UserService to depend on the abstraction. UserService confirms the UserRepository protocol.
  4. Use the concrete implementation when creating instances.

As we can see we should be careful to solve any problem and look for all potential problems with the code. Initially, We solved the dependency injection problem but introduced a violation of DIP.


Enjoy a sunny Monday.

Rashida begam Shaik

Sr iOS developer | Swift | Swift UI | Objective C | Flutter

4 个月

Very well explained and explained in a easy and understandable way... Thanks for the post..

回复
Alassane DIOP

Tech Lead .NET, C# | Software Engineer at Societe Generale Securities Services - SGSS

10 个月

Such a nice article ?????? Dependency injection need to be well understood in order to be comfortable with others design pattern

要查看或添加评论,请登录

Rishabh Yadav的更多文章

  • Understanding OAuth 2.0 and PKCE

    Understanding OAuth 2.0 and PKCE

    What is OAuth 2.0? OAuth 2.

  • Transforming Software Development: How OpenAI is Revolutionizing the Industry

    Transforming Software Development: How OpenAI is Revolutionizing the Industry

    OpenAI's advancements in artificial intelligence, particularly through large language models like GPT-4, are poised to…

  • Social Fun Game(Node.js & ReactJS)

    Social Fun Game(Node.js & ReactJS)

    As mentioned Earlier, I have started to work on Backend programming through Node.js.

  • State Management in React Native

    State Management in React Native

    Hello Everyone, Today, We are going to delve into state management in react native. This is always a hot discussion…

  • React Native new Architecture

    React Native new Architecture

    Hello everyone, Today, we're diving into the new architecture of React Native. But before we do, let's take a glance at…

  • Singleton: Good vs bad

    Singleton: Good vs bad

    The Singleton pattern has been debated in software engineering, with proponents and critics offering various…

  • React Server Component

    React Server Component

    Hi Everyone, Today, we are going to delve into the React server component. Project/Product managers often ask me about…

  • Closures in Swift

    Closures in Swift

    Closures in Swift are self-contained blocks of functionality that can be passed around and used in your code. They are…

  • MVVM in Swift UI

    MVVM in Swift UI

    Hello Everyone, Today, We are going to see a simple app with MVVM architecture. What is MVVM? Model-View-ViewModel…

  • Concurrency & Multithreading in Swift

    Concurrency & Multithreading in Swift

    Hello Everyone, Today we are going to delve into multithreading or concurrency in Swift. let's see the below example…

    2 条评论