Swift带associatedtype协议实现依赖倒置的相关问题咨询
1. 依赖倒置实现正确性判断
你的设计思路完全符合依赖倒置原则的核心要求:上层业务组件(ViewModel)不依赖底层具体实现(Networker),而是依赖抽象协议(Networkable),底层实现的变更不会影响上层业务逻辑。目前代码的报错是Swift语法层面的限制:带关联类型的协议无法直接作为属性、参数的类型使用,只能作为泛型约束,和设计逻辑的正确性无关。
2. 保留泛型特性同时支持测试的实现方案
核心解法:为带关联类型的Networkable协议实现类型擦除容器
类型擦除的作用就是把带关联类型的协议包装成具体的泛型类,消除Swift的语法限制,同时完全保留泛型特性和协议的抽象能力。
第一步:实现通用类型擦除容器
final class AnyNetworker<Response: Decodable>: Networkable { // 存储传入的具体实现的request方法 private let requestHandler: (@escaping (Result<Response, Error>) -> Void) -> Void // 初始化时接受任意遵守Networkable协议的实例,绑定Response类型 init<T: Networkable>(_ origin: T) where T.Response == Response { requestHandler = origin.request } func request(handler: @escaping (Result<Response, Error>) -> Void) { requestHandler(handler) } }
第二步:改造ViewModel适配泛型
根据你的业务场景选择以下两种改造方式之一即可:
- 如果你当前ViewModel只处理固定的返回模型,直接指定Response类型:
class ViewModel { let networker: AnyNetworker<ResponseModel> init(netwrorker: AnyNetworker<ResponseModel>) { self.networker = netwrorker } func execute() { networker.request { _ in print("Responsed") } } }
- 如果需要ViewModel支持任意返回模型,改成泛型类:
class ViewModel<Response: Decodable> { let networker: AnyNetworker<Response> init(netwrorker: AnyNetworker<Response>) { self.networker = netwrorker } func execute() { networker.request { _ in print("Responsed") } } }
第三步:业务调用
只需要把原来的Networker实例用AnyNetworker包装一层即可:
class View { let viewModel: ViewModel init() { let concreteNetworker = Networker<ResponseModel>(session: .shared, url: URL(string: "SOME URL")!) // 包装为类型擦除容器 let wrappedNetworker = AnyNetworker(concreteNetworker) self.viewModel = ViewModel(netwrorker: wrappedNetworker) } func load() { viewModel.execute() } } struct ResponseModel: Decodable {}
测试支持
测试时只需要实现Mock版的Networkable,不需要修改任何ViewModel代码:
final class MockNetworker<Response: Decodable>: Networkable { // 自定义 mock 返回结果 let mockResult: Result<Response, Error> init(mockResult: Result<Response, Error>) { self.mockResult = mockResult } func request(handler: @escaping (Result<Response, Error>) -> Void) { // 直接返回 mock 结果,不需要发起真实网络请求 handler(mockResult) } } // 测试用例示例 func testViewModelRequestSuccess() { let mockResponse = ResponseModel() let mockNetworker = MockNetworker(mockResult: .success(mockResponse)) let wrappedMock = AnyNetworker(mockNetworker) let testViewModel = ViewModel(netwrorker: wrappedMock) // 后续执行测试逻辑即可 }
内容的提问来源于stack exchange,提问作者Dawood Mujib
相关产品推荐
相关产品推荐

