组件化解耦

38 阅读7分钟

我们有一个Base组件,其中包含BaseVC,但是BaseVC中有很多业务逻辑,这些业务逻辑原本在Common中,现在由于依赖关系(Common依赖Base,而Base不能依赖Common)无法直接使用。
解决方案:

  1. 将BaseVC中的业务逻辑抽离出来,放到Common中,BaseVC只保留非常基础的功能(比如生命周期管理、基础UI配置等)。
  2. 如果有些业务逻辑确实与BaseVC紧密相关,且不能放到Common中,那么我们可以考虑使用依赖注入或者协议的方式来解耦。

但是,现在的问题是Base组件不能依赖Common,而Common依赖Base。所以,我们可以在Base中定义协议,然后在Common中实现这些协议,并通过依赖注入的方式将实现注入到Base中。

具体步骤:

方案一:抽离业务逻辑

  • 将BaseVC中涉及业务逻辑的代码移到Common中的某个类中,然后在BaseVC中通过属性持有这个类的实例,并在需要的时候调用。这样,BaseVC就只依赖一个抽象的协议,而具体的实现由Common提供。

方案二:使用协议和依赖注入

  • 在Base中定义业务逻辑需要的协议。
  • 在Common中实现这些协议。
  • 在App启动时,将协议的实现实例注入到BaseVC中(或者通过ServiceLocator模式、依赖注入容器等方式)。

下面我们以方案二为例,给出一个具体的例子:

假设BaseVC中需要处理用户登录状态,原本直接使用了Common中的UserManager,现在我们要解耦。

步骤1:在Base中定义协议

swift

// Base组件中
public protocol UserManagerProtocol: AnyObject {
    var isLoggedIn: Bool { get }
    func login()
    func logout()
}

步骤2:在BaseVC中持有协议类型的属性

swift

// Base组件中
open class BaseVC: UIViewController {
    // 通过依赖注入,默认可以通过ServiceLocator获取,或者由子类设置
    public var userManager: UserManagerProtocol?

    open func checkLogin() -> Bool {
        return userManager?.isLoggedIn ?? false
    }
}

步骤3:在Common中实现协议

swift

// Common组件中
import Base

public class UserManager: UserManagerProtocol {
    public static let shared = UserManager()

    public var isLoggedIn: Bool {
        // 返回登录状态
        return true
    }

    public func login() {
        // 登录逻辑
    }

    public func logout() {
        // 退出逻辑
    }
}

步骤4:在App启动时,设置BaseVC的默认userManager
我们可以在AppDelegate中,或者Common组件中提供一个设置方法,将UserManager.shared赋值给BaseVC的userManager。但是注意,BaseVC的userManager是可选类型,我们需要在合适的地方注入。

我们可以通过方法交换或基类初始化方法来实现,但方法交换不太安全。我们可以在BaseVC中提供一个类方法用来设置默认的UserManager。

或者,我们可以在Common组件中创建一个设置函数,在App启动时调用:

swift

// Common组件中
public func setupBaseVC() {
    BaseVC.globalUserManager = UserManager.shared
}

// Base组件中
open class BaseVC: UIViewController {
    public static var globalUserManager: UserManagerProtocol?

    public var userManager: UserManagerProtocol? {
        get {
            return _userManager ?? BaseVC.globalUserManager
        }
        set {
            _userManager = newValue
        }
    }
    private var _userManager: UserManagerProtocol?
}

这样,如果没有单独设置,就会使用全局的globalUserManager。

方案三:使用服务发现(Service Locator)模式
我们可以在Base组件中创建一个服务定位器,然后在Common组件中注册服务。

步骤1:在Base组件中创建服务定位器

swift

// Base组件中
public class ServiceLocator {
    public static let shared = ServiceLocator()
    private var services: [String: Any] = [:]

    public func register<Service>(_ service: Service, for type: Service.Type) {
        services[String(describing: type)] = service
    }

    public func resolve<Service>(_ type: Service.Type) -> Service? {
        return services[String(describing: type)] as? Service
    }
}

步骤2:在BaseVC中通过服务定位器获取服务

swift

// Base组件中
open class BaseVC: UIViewController {
    public var userManager: UserManagerProtocol? {
        return ServiceLocator.shared.resolve(UserManagerProtocol.self)
    }
}

步骤3:在Common组件中注册服务

swift

// Common组件中
import Base

public class UserManager: UserManagerProtocol {
    // ... 实现
}

// 在App启动时注册
ServiceLocator.shared.register(UserManager.shared, for: UserManagerProtocol.self)

以上三种方案,我们可以根据实际情况选择。

另外,如果BaseVC中的业务逻辑非常多,我们可能需要考虑重构,将业务逻辑抽离到单独的类中,BaseVC只负责视图生命周期和基础UI,业务逻辑通过依赖注入的方式引入。

最后,我们还可以考虑将Base组件拆分为两个组件:BaseCore和BaseUI。BaseCore包含一些最基础的协议和工具,BaseUI包含BaseVC等UI基类,这样Common可以依赖BaseCore,而BaseUI依赖Common(或者通过协议解耦)。但是这样可能会增加组件数量,需要根据项目情况权衡。

由于时间关系,这里只提供了几种思路和示例代码,具体实施需要根据项目结构进行调整。

你遇到的这个问题很典型:BaseVC 放在基础组件中,但包含了很多业务逻辑,又不能依赖 Common。我来提供几种解决方案:

方案一:依赖注入 + 协议抽象(推荐)

1. 在 Base 组件中定义协议

swift

// Base/Protocols/BusinessProtocols.swift
public protocol UserInfoProvider {
    var currentUser: User? { get }
    func updateUserInfo(_ user: User)
}

public protocol CacheProvider {
    func getCachedData(for key: String) -> Any?
    func setCachedData(_ data: Any?, for key: String)
}

public protocol NetworkProvider {
    func request(_ endpoint: String, completion: @escaping (Result<Data, Error>) -> Void)
}

2. BaseVC 通过协议使用业务功能

swift

// Base/Classes/BaseViewController.swift
open class BaseViewController: UIViewController {
    
    // 通过协议引用业务功能,不依赖具体实现
    public weak var userInfoProvider: UserInfoProvider?
    public weak var cacheProvider: CacheProvider?
    public weak var networkProvider: NetworkProvider?
    
    // 提供默认空实现,避免崩溃
    open var currentUser: User? {
        return userInfoProvider?.currentUser
    }
    
    open func showUserProfile() {
        guard let user = currentUser else {
            // 处理未登录情况
            showLogin()
            return
        }
        // 显示用户信息
    }
    
    open func cacheData(_ data: Any?, for key: String) {
        cacheProvider?.setCachedData(data, for: key)
    }
    
    open func requestData(_ endpoint: String, completion: @escaping (Result<Data, Error>) -> Void) {
        networkProvider?.request(endpoint, completion: completion)
    }
    
    // 需要子类重写的方法
    open func showLogin() {
        // 基类空实现,由业务VC重写
    }
}

3. 在 Common 中实现协议

swift

// Common/Classes/ProtocolImplementations.swift
public class CommonUserInfoProvider: UserInfoProvider {
    public var currentUser: User? {
        return UserManager.shared.currentUser
    }
    
    public func updateUserInfo(_ user: User) {
        UserManager.shared.updateUser(user)
    }
}

public class CommonCacheProvider: CacheProvider {
    public func getCachedData(for key: String) -> Any? {
        return CacheManager.shared.object(forKey: key)
    }
    
    public func setCachedData(_ data: Any?, for key: String) {
        CacheManager.shared.setObject(data, forKey: key)
    }
}

4. 在 App 启动时注入依赖

swift

// AppDelegate 或专门的配置类
class AppDependencyConfigurator {
    static func configure() {
        let baseVC = BaseViewController.appearance()
        
        // 注入 Common 中的具体实现
        baseVC.userInfoProvider = CommonUserInfoProvider()
        baseVC.cacheProvider = CommonCacheProvider()
        baseVC.networkProvider = CommonNetworkProvider()
    }
}

方案二:模板方法模式

1. BaseVC 定义抽象方法

swift

// Base/Classes/BaseViewController.swift
open class BaseViewController: UIViewController {
    
    // 模板方法 - 具体实现在子类中
    open func getCurrentUser() -> User? {
        fatalError("子类必须重写此方法")
    }
    
    open func cacheData(_ data: Any?, for key: String) {
        fatalError("子类必须重写此方法")
    }
    
    open func getCachedData(for key: String) -> Any? {
        fatalError("子类必须重写此方法")
    }
    
    // 具体方法 - 使用抽象方法
    open func refreshUserData() {
        guard let user = getCurrentUser() else {
            showLogin()
            return
        }
        // 使用用户数据
    }
}

2. 在 Common 中创建业务基类

swift

// Common/Classes/CommonBaseViewController.swift
open class CommonBaseViewController: BaseViewController {
    
    // 实现 BaseVC 的抽象方法
    open override func getCurrentUser() -> User? {
        return UserManager.shared.currentUser
    }
    
    open override func cacheData(_ data: Any?, for key: String) {
        CacheManager.shared.setObject(data, forKey: key)
    }
    
    open override func getCachedData(for key: String) -> Any? {
        return CacheManager.shared.object(forKey: key)
    }
    
    // 业务特定的方法
    open func commonBusinessMethod() {
        // 使用 Common 中的功能
    }
}

方案三:事件总线通信

1. 在 Base 中定义事件系统

swift

// Base/Classes/EventBus.swift
public class EventBus {
    public static let shared = EventBus()
    
    private var observers: [String: [(Any?) -> Void]] = [:]
    
    public func publish(_ event: String, data: Any? = nil) {
        observers[event]?.forEach { $0(data) }
    }
    
    public func subscribe(_ event: String, handler: @escaping (Any?) -> Void) {
        if observers[event] == nil {
            observers[event] = []
        }
        observers[event]?.append(handler)
    }
}

// Base/Classes/Events.swift
public struct BaseEvents {
    public static let getUserInfo = "base.get_user_info"
    public static let cacheData = "base.cache_data"
    public static let getCachedData = "base.get_cached_data"
}

2. BaseVC 通过事件获取数据

swift

// Base/Classes/BaseViewController.swift
open class BaseViewController: UIViewController {
    
    open func getCurrentUser() -> User? {
        var result: User?
        let semaphore = DispatchSemaphore(value: 0)
        
        EventBus.shared.subscribe(BaseEvents.getUserInfo) { data in
            result = data as? User
            semaphore.signal()
        }
        
        EventBus.shared.publish(BaseEvents.getUserInfo)
        _ = semaphore.wait(timeout: .now() + 1.0)
        
        return result
    }
}

3. Common 中监听并响应事件

swift

// Common/Classes/EventHandlers.swift
public class CommonEventHandlers {
    public static func setup() {
        // 响应获取用户信息事件
        EventBus.shared.subscribe(BaseEvents.getUserInfo) { _ in
            let user = UserManager.shared.currentUser
            EventBus.shared.publish(BaseEvents.getUserInfo, data: user)
        }
        
        // 响应缓存事件
        EventBus.shared.subscribe(BaseEvents.cacheData) { data in
            if let dict = data as? [String: Any],
               let key = dict["key"] as? String {
                CacheManager.shared.setObject(dict["value"], forKey: key)
            }
        }
    }
}

方案四:服务定位器模式

1. Base 中定义服务定位器

swift

// Base/Classes/ServiceLocator.swift
public class ServiceLocator {
    public static let shared = ServiceLocator()
    private var services: [String: Any] = [:]
    
    public func register<Service>(_ service: Service, for type: Service.Type) {
        services[String(describing: type)] = service
    }
    
    public func service<Service>(for type: Service.Type) -> Service? {
        return services[String(describing: type)] as? Service
    }
}

// Base/Classes/Services.swift
public protocol UserService {
    var currentUser: User? { get }
    func updateUser(_ user: User)
}

public protocol CacheService {
    func setObject(_ object: Any?, forKey key: String)
    func object(forKey key: String) -> Any?
}

2. BaseVC 通过服务定位器获取服务

swift

// Base/Classes/BaseViewController.swift
open class BaseViewController: UIViewController {
    
    open var userService: UserService? {
        return ServiceLocator.shared.service(for: UserService.self)
    }
    
    open var cacheService: CacheService? {
        return ServiceLocator.shared.service(for: CacheService.self)
    }
    
    open func businessMethod() {
        if let user = userService?.currentUser {
            // 使用用户数据
        }
    }
}

3. Common 中注册服务实现

swift

// Common/Classes/ServiceImplementations.swift
public class CommonUserService: UserService {
    public var currentUser: User? {
        return UserManager.shared.currentUser
    }
    
    public func updateUser(_ user: User) {
        UserManager.shared.updateUser(user)
    }
}

// 在 App 启动时注册
ServiceLocator.shared.register(CommonUserService(), for: UserService.self)