In software engineering, dependency injection is a design pattern that involves providing an object with its dependencies, rather than having the object create or manage them itself. There are three main types of dependency injection: constructor injection, property injection, and method injection.
Constructor injection
Constructor injection involves passing the dependencies to the object's constructor, so that the object can be instantiated with its dependencies already in place. This is often considered the most explicit and straightforward way of injecting dependencies, since it makes the dependencies of the object explicit and requires the caller to provide them at the time the object is created.
Example in Swift:
class UserService {
private let userRepository: UserRepository
init(userRepository: UserRepository) {
self.userRepository = userRepository
}
func getUserById(id: Long) -> User {
return userRepository.findById(id: id)
}
}
In this example, the UserService class has a dependency on a UserRepository object, which it uses to find users by their ID. Instead of creating or managing the UserRepository itself, the UserService uses constructor injection to receive the UserRepository from the caller. This is done by providing a initializer that accepts a UserRepository as a parameter, which the caller can use to pass the UserRepository to the UserService when creating a new instance of the class. This allows the UserService to use the UserRepository without having to create or manage it itself, making the code more modular and easier to test.
Property injection
Property injection involves setting the dependencies of an object using its public properties, either directly or via a setter method. This is a less explicit approach than constructor injection, since it doesn't require the caller to provide the dependencies at the time the object is created. However, it can still make the dependencies of the object clear and easy to manage.
class UserService {
var userRepository: UserRepository?
func getUserById(id: Long) -> User {
guard let userRepository = userRepository else {
return User()
}
return userRepository.findById(id: id)
}
}
Similar example to the Constructor injection, the UserService class has a dependency on a UserRepository object, which it uses to find users by their ID. Instead of creating or managing the UserRepository itself, the UserService uses property injection to receive the UserRepository from the caller. This is done by providing a userRepository property, which the caller can use to set the UserRepository on the UserService before calling any other methods. The UserService then checks if the userRepository property is set before using it, and returns a default value if it is not set. This allows the UserService to use the UserRepository without having to create or manage it itself, making the code more modular and easier to test.
Method injection
Method injection involves passing the dependencies to a specific method of the object, rather than to the object's constructor or properties. This can be useful in situations where an object needs to use a dependency only for a specific operation, rather than for its entire lifetime. It can also make it easier to test objects, since the dependencies can be mocked or stubbed out in test cases.
Similar example in Swift:
class UserService {
private var userRepository: UserRepository?
func setUserRepository(userRepository: UserRepository) {
self.userRepository = userRepository
}
func getUserById(id: Long) -> User {
guard let userRepository = userRepository else {
return User()
}
return userRepository.findById(id: id)
}
}
In this example, the UserService class has a dependency on a UserRepository object, which it uses to find users by their ID. Instead of creating or managing the UserRepository itself, the UserService uses method injection to receive the UserRepository from the caller. This is done by providing a setUserRepository method, which the caller can use to pass the UserRepository to the UserService before calling any other methods. The UserService then stores the UserRepository in a private property, and checks if it is set before using it in the getUserById method. This allows the UserService to use the UserRepository without having to create or manage it itself, making the code more modular and easier to test.
Conclusion
Overall, the choice of which type of dependency injection to use will depend on the specific needs and requirements of your application. Each type has its own advantages and disadvantages, and the right choice will depend on the context in which the object is being used.
Reference
https://needone.app/contructor-injection-property-injection-or-method-injection/
Top comments (0)