You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 01:36:02