依赖注入(Dependency Injection, DI)在 iOS 开发中的应用

打印 上一主题 下一主题

主题 796|帖子 796|积分 2388

在 iOS 开发中,我们经常会遇到类与类之间存在依赖关系的情况。比方,一个视图控制器大概需要一个服务对象来处理数据,这种情况下,视图控制器就依赖于这个服务对象。传统的做法是直接在视图控制器中创建服务对象,但这会导致类之间的紧密耦合,降低代码的可维护性和可测试性。为了改善这一点,我们可以利用依赖注入(Dependency Injection, DI)模式。

入门

什么是依赖注入?

依赖注入是一种计划模式,用于清除对象之间的依赖关系。通过依赖注入,一个类所依赖的对象(即依赖)由外部通报给它,而不是在类内部自己创建。如允许以降低类之间的耦合度,提高代码的可维护性和可测试性。
依赖注入的类型

依赖注入重要有三种类型:构造函数注入、属性注入和方法注入。
1. 构造函数注入

构造函数注入是通过构造函数将依赖对象通报给类。构造函数注入通常是最常用和保举的方式,由于依赖在对象创建时就被注入,从而确保了对象的完整性。
  1. class AuthService {
  2.     private let userManager: UserManagerProtocol
  3.     init(userManager: UserManagerProtocol) {
  4.         self.userManager = userManager
  5.     }
  6.     func authenticate() {
  7.         guard let user = userManager.currentUser else {
  8.             print("No user to authenticate")
  9.             return
  10.         }
  11.         print("Authenticating user: \(user.name)")
  12.     }
  13. }
复制代码
2. 属性注入

属性注入是通过设置类的属性将依赖对象通报给类。属性注入允许在对象创建之后再注入依赖,适用于那些在对象创建时不需要立即利用依赖的情况。
  1. class AuthService {
  2.     var userManager: UserManagerProtocol?
  3.     func authenticate() {
  4.         guard let userManager = userManager, let user = userManager.currentUser else {
  5.             print("No user to authenticate")
  6.             return
  7.         }
  8.         print("Authenticating user: \(user.name)")
  9.     }
  10. }
  11. // 使用时
  12. let authService = AuthService()
  13. authService.userManager = UserManager.shared
  14. authService.authenticate()
复制代码
3. 方法注入

方法注入是通过方法参数将依赖对象通报给类。方法注入适用于那些只在特定方法调用时才需要依赖的情况。
  1. class AuthService {
  2.     func authenticate(userManager: UserManagerProtocol) {
  3.         guard let user = userManager.currentUser else {
  4.             print("No user to authenticate")
  5.             return
  6.         }
  7.         print("Authenticating user: \(user.name)")
  8.     }
  9. }
  10. // 使用时
  11. let authService = AuthService()
  12. authService.authenticate(userManager: UserManager.shared)
复制代码
依赖注入的利益


  • 提高代码的可测试性:通过依赖注入,可以轻松地替换依赖对象,从而进行单元测试。比方,可以在测试中注入一个模拟对象(mock object)来验证依赖类的行为。
  • 减少类之间的耦合:依赖注入使类只依赖于接口或抽象类,而不是具体实现,从而降低了类之间的耦合度。
  • 提高代码的机动性和复用性:通过依赖注入,可以轻松地替换依赖对象,从而实现差别的功能或行为。比方,可以注入差别的实现来实现差别的策略模式。
利用依赖注入框架

在实际开发中,我们可以利用依赖注入框架来管理和注入依赖对象。Swinject 是一个盛行的 Swift 语言依赖注入框架,可以资助我们轻松实现依赖注入。
Swinject 示例

以下是一个利用 Swinject 的简单示例:
  1. import Swinject
  2. // 定义协议
  3. protocol UserManagerProtocol {
  4.     var currentUser: UserProtocol? { get }
  5. }
  6. // 定义实现类
  7. class UserManager: UserManagerProtocol {
  8.     static let shared = UserManager()
  9.     var currentUser: UserProtocol?
  10.     private init() {}
  11. }
  12. protocol AuthServiceProtocol {
  13.     func authenticate()
  14. }
  15. class AuthService: AuthServiceProtocol {
  16.     private let userManager: UserManagerProtocol
  17.     init(userManager: UserManagerProtocol) {
  18.         self.userManager = userManager
  19.     }
  20.     func authenticate() {
  21.         guard let user = userManager.currentUser else {
  22.             print("No user to authenticate")
  23.             return
  24.         }
  25.         print("Authenticating user: \(user.name)")
  26.     }
  27. }
  28. // 配置 Swinject 容器
  29. let container = Container()
  30. container.register(UserManagerProtocol.self) { _ in UserManager.shared }
  31. container.register(AuthServiceProtocol.self) { r in
  32.     AuthService(userManager: r.resolve(UserManagerProtocol.self)!)
  33. }
  34. // 使用依赖注入
  35. let authService = container.resolve(AuthServiceProtocol.self)!
  36. authService.authenticate()
复制代码
在这个示例中,我们通过 Swinject 框架设置了依赖注入容器,将 UserManager 和 AuthService 注册到容器中,并在需要利用时解析依赖。
小结

依赖注入是一种强盛的计划模式,可以显著提高代码的可维护性和可测试性。通过引入依赖注入,我们可以清除对象之间的紧密耦合,使代码更加机动和可扩展。希望本文对你理解和应用依赖注入有所资助,并能在实际开发中充分利用这一模式来改善你的代码质量。

对 Swinject 深入相识

上文“设置 Swinject 容器”和“利用依赖注入”部分的代码,对于新手来说大概并不友爱,如下是对其具体的解释和分析
设置 Swinject 容器

首先,我们需要设置一个 Swinject 容器,用于管理依赖关系。容器会存储对象及其依赖关系,并在需要时提供这些对象。
  1. import Swinject
  2. // 配置 Swinject 容器
  3. let container = Container()
  4. // 注册 UserManagerProtocol 类型
  5. container.register(UserManagerProtocol.self) { _ in
  6.     UserManager.shared
  7. }
  8. // 注册 AuthServiceProtocol 类型
  9. container.register(AuthServiceProtocol.self) { resolver in
  10.     AuthService(userManager: resolver.resolve(UserManagerProtocol.self)!)
  11. }
复制代码
代码解释


  • 创建容器:let container = Container() 创建了一个 Swinject 容器。
  • 注册 UserManagerProtocol:container.register(UserManagerProtocol.self) { _ in UserManager.shared } 向容器注册 UserManagerProtocol 类型。当需要 UserManagerProtocol 的实例时,容器会提供 UserManager.shared 实例。
  • 注册 AuthServiceProtocol:container.register(AuthServiceProtocol.self) { resolver in AuthService(userManager: resolver.resolve(UserManagerProtocol.self)!) } 向容器注册 AuthServiceProtocol 类型。当需要 AuthServiceProtocol 的实例时,容器会创建一个 AuthService 实例,并利用 resolver.resolve(UserManagerProtocol.self)! 获取 UserManagerProtocol 的实例作为其依赖。
利用依赖注入

设置好容器后,我们可以从容器中解析(获取)依赖对象,并利用它们。
  1. // 使用依赖注入
  2. let authService = container.resolve(AuthServiceProtocol.self)!
  3. authService.authenticate()
复制代码
代码解释


  • 解析 AuthServiceProtocol:let authService = container.resolve(AuthServiceProtocol.self)! 从容器中解析 AuthServiceProtocol 的实例。容器会根据之前的注册信息,提供一个 AuthService 实例。
  • 利用 AuthService:authService.authenticate() 调用 AuthService 的 authenticate 方法。
完整示例

以下是完整的代码示例,展示怎样设置 Swinject 容器和利用依赖注入:
  1. import Swinject// 界说协议protocol UserManagerProtocol {    var currentUser: UserProtocol? { get }}// 界说实现类class UserManager: UserManagerProtocol {    static let shared = UserManager()    var currentUser: UserProtocol?    private init() {}}protocol AuthServiceProtocol {    func authenticate()}class AuthService: AuthServiceProtocol {    private let userManager: UserManagerProtocol    init(userManager: UserManagerProtocol) {        self.userManager = userManager    }    func authenticate() {        guard let user = userManager.currentUser else {            print("No user to authenticate")            return        }        print("Authenticating user: \(user.name)")    }}// 设置 Swinject 容器let container = Container()// 注册 UserManagerProtocol 类型container.register(UserManagerProtocol.self) { _ in    UserManager.shared}// 注册 AuthServiceProtocol 类型container.register(AuthServiceProtocol.self) { resolver in    AuthService(userManager: resolver.resolve(UserManagerProtocol.self)!)}// 使用依赖注入
  2. let authService = container.resolve(AuthServiceProtocol.self)!
  3. authService.authenticate()
复制代码
小结

通过 Swinject 容器,我们可以轻松地管理对象及其依赖关系,并在需要时解析这些对象。这种方式可以清除类之间的紧密耦合,使代码更具可维护性和可测试性。假如你有任何进一步的问题,请随时提出!

俯瞰 DI 在组件化项目中的位置


   下面是完整的代码示例,明白注明每部分代码应放在哪个组件中。
  CommonModule

UserProtocol.swift

  1. public protocol UserProtocol {
  2.     var name: String { get }
  3. }
  4. public protocol UserManagerProtocol {
  5.     var currentUser: UserProtocol? { get }
  6. }
  7. public protocol AuthServiceProtocol {
  8.     func authenticate()
  9. }
复制代码
AuthComponent

AuthService.swift

  1. import CommonModule
  2. public class AuthService: AuthServiceProtocol {
  3.     private let userManager: UserManagerProtocol
  4.     public init(userManager: UserManagerProtocol) {
  5.         self.userManager = userManager
  6.     }
  7.     public func authenticate() {
  8.         guard let user = userManager.currentUser else {
  9.             print("No user to authenticate")
  10.             return
  11.         }
  12.         print("Authenticating user: \(user.name)")
  13.     }
  14. }
复制代码
AuthComponent.podspec

  1. Pod::Spec.new do |s|
  2.   s.name         = 'AuthComponent'
  3.   s.version      = '1.0.0'
  4.   s.summary      = 'A short description of AuthComponent.'
  5.   s.description  = <<-DESC
  6.                    A longer description of AuthComponent.
  7.                    DESC
  8.   s.homepage     = 'https://example.com/AuthComponent'
  9.   s.license      = { :type => 'MIT', :file => 'LICENSE' }
  10.   s.author       = { 'Your Name' => 'you@example.com' }
  11.   s.source       = { :git => 'https://github.com/yourname/AuthComponent.git', :tag => s.version.to_s }
  12.   s.ios.deployment_target = '10.0'
  13.   s.source_files  = 'Sources/**/*.{h,m,swift}'
  14.   # 依赖 CommonModule
  15.   s.dependency 'CommonModule', '~> 1.0.0'
  16. end
复制代码
UserComponent

UserManager.swift

  1. import CommonModule
  2. public class UserManager: UserManagerProtocol {
  3.     public static let shared = UserManager()
  4.     public var currentUser: UserProtocol?
  5.     private init() {}
  6. }
复制代码
UserComponent.podspec

  1. Pod::Spec.new do |s|
  2.   s.name         = 'UserComponent'
  3.   s.version      = '1.0.0'
  4.   s.summary      = 'A short description of UserComponent.'
  5.   s.description  = <<-DESC
  6.                    A longer description of UserComponent.
  7.                    DESC
  8.   s.homepage     = 'https://example.com/UserComponent'
  9.   s.license      = { :type => 'MIT', :file => 'LICENSE' }
  10.   s.author       = { 'Your Name' => 'you@example.com' }
  11.   s.source       = { :git => 'https://github.com/yourname/UserComponent.git', :tag => s.version.to_s }
  12.   s.ios.deployment_target = '10.0'
  13.   s.source_files  = 'Sources/**/*.{h,m,swift}'
  14.   # 依赖 CommonModule 和 AuthComponent
  15.   s.dependency 'CommonModule', '~> 1.0.0'
  16.   s.dependency 'AuthComponent', '~> 1.0.0'
  17. end
复制代码
Example Project

Podfile

  1. platform :ios, '10.0'
  2. target 'ExampleApp' do
  3.   use_frameworks!
  4.   # 使用 UserComponent 和 AuthComponent
  5.   pod 'UserComponent', :path => '../UserComponent'
  6.   pod 'AuthComponent', :path => '../AuthComponent'
  7. end
复制代码
main.swift (主工程的任意合适位置)

  1. import Swinjectimport CommonModuleimport UserComponentimport AuthComponent// 设置 Swinject 容器let container = Container()// 注册 UserManagerProtocol 类型container.register(UserManagerProtocol.self) { _ in    UserManager.shared}// 注册 AuthServiceProtocol 类型container.register(AuthServiceProtocol.self) { resolver in    AuthService(userManager: resolver.resolve(UserManagerProtocol.self)!)}// 使用依赖注入
  2. let authService = container.resolve(AuthServiceProtocol.self)!
  3. authService.authenticate()
复制代码
项目结构

  1. CommonModule/
  2. ├── UserProtocol.swift
  3. AuthComponent/
  4. ├── AuthComponent.podspec
  5. ├── Sources/
  6. │   └── AuthService.swift
  7. UserComponent/
  8. ├── UserComponent.podspec
  9. ├── Sources/
  10. │   └── UserManager.swift
  11. ExampleApp/
  12. ├── Podfile
  13. ├── main.swift
复制代码
说明


  • CommonModule:界说了协议 UserProtocol、UserManagerProtocol 和 AuthServiceProtocol。
  • AuthComponent:实现了 AuthService,并在 podspec 文件中声明白对 CommonModule 的依赖。
  • UserComponent:实现了 UserManager,并在 podspec 文件中声明白对 CommonModule 和 AuthComponent 的依赖。
  • Example Project:利用 Podfile 将 UserComponent 和 AuthComponent 加入工程,并设置 Swinject 容器来注入依赖。
通过这些设置和构造,我们可以或许实现依赖注入,从而使代码更加模块化和可维护。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

傲渊山岳

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表