Tailwind CSSTailwind CSS
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain

第4章:协议与泛型的结合

4.5 实战案例:设计一个泛型的网络请求框架

在前几节中,我们探讨了泛型、协议约束、关联类型和 Self 的用法,这些概念为构建灵活、可复用的代码奠定了基础。本节将通过一个实战案例,将这些知识整合应用,设计一个泛型的网络请求框架。该框架将支持多种数据类型、解耦网络实现,并提供类型安全的响应处理,充分展示协议化编程(POP)与泛型的协同能力。

案例背景

假设我们要开发一个应用,需要从服务器获取不同类型的数据(如用户信息、文章列表等)。我们希望框架具备以下特点:

  • 类型安全:响应数据自动解析为指定类型。
  • 可扩展性:支持不同网络实现(如 URLSession 或第三方库)。
  • 复用性:通用的请求逻辑适用于多种数据类型。
  • 解耦性:网络层与数据模型分离。

设计协议

首先,定义核心协议:

// 表示可请求的数据
protocol Requestable {
    associatedtype Response
    var endpoint: String { get }
    func parse(data: Data) throws -> Response
}

// 表示网络服务的抽象
protocol NetworkService {
    func fetch<T: Requestable>(_ request: T, completion: @escaping (Result<T.Response, Error>) -> Void)
}
  • Requestable 使用关联类型 Response 表示响应数据类型,并定义了解析方法。
  • NetworkService 定义了通用的网络请求接口,泛型约束确保请求类型遵循 Requestable。

实现网络服务

提供一个基于 URLSession 的默认网络服务实现:

struct URLSessionService: NetworkService {
    func fetch<T: Requestable>(_ request: T, completion: @escaping (Result<T.Response, Error>) -> Void) {
        guard let url = URL(string: "https://api.example.com\(request.endpoint)") else {
            completion(.failure(NetworkError.invalidURL))
            return
        }
        
        URLSession.shared.dataTask(with: url) { data, _, error in
            if let error = error {
                completion(.failure(error))
                return
            }
            
            guard let data = data else {
                completion(.failure(NetworkError.noData))
                return
            }
            
            do {
                let result = try request.parse(data: data)
                completion(.success(result))
            } catch {
                completion(.failure(error))
            }
        }.resume()
    }
}

enum NetworkError: Error {
    case invalidURL
    case noData
}
  • fetch 使用泛型 T: Requestable,确保请求对象提供 endpoint 和 parse。
  • 响应通过 Result 类型返回,保持类型安全。

定义具体请求

创建几个具体的请求类型,遵循 Requestable:

struct UserRequest: Requestable {
    var endpoint: String { "/users/\(userId)" }
    let userId: String
    
    func parse(data: Data) throws -> User {
        return try JSONDecoder().decode(User.self, from: data)
    }
}

struct ArticleRequest: Requestable {
    var endpoint: String { "/articles" }
    
    func parse(data: Data) throws -> [Article] {
        return try JSONDecoder().decode([Article].self, from: data)
    }
}

struct User: Codable {
    let id: String
    let name: String
}

struct Article: Codable {
    let title: String
    let content: String
}
  • UserRequest 获取单个用户信息,解析为 User 类型。
  • ArticleRequest 获取文章列表,解析为 [Article] 类型。

使用框架

测试框架的使用方式:

let service = URLSessionService()

let userRequest = UserRequest(userId: "001")
service.fetch(userRequest) { result in
    switch result {
    case .success(let user):
        print("User: \(user.name)") // 假设返回: User: Alice
    case .failure(let error):
        print("Error: \(error)")
    }
}

let articleRequest = ArticleRequest()
service.fetch(articleRequest) { result in
    switch result {
    case .success(let articles):
        print("Articles: \(articles.map { $0.title })")
    case .failure(let error):
        print("Error: \(error)")
    }
}
  • 框架自动推断 Response 类型(User 或 [Article]),提供类型安全的结果。

添加协议扩展

为 Requestable 添加默认实现,简化常见操作:

extension Requestable {
    func defaultHeaders() -> [String: String] {
        return ["Content-Type": "application/json"]
    }
}

extension Requestable where Response: Codable {
    func parse(data: Data) throws -> Response {
        return try JSONDecoder().decode(Response.self, from: data)
    }
}
  • defaultHeaders() 提供通用的 HTTP 头。
  • parse(data:) 为 Codable 类型提供默认解析,UserRequest 和 ArticleRequest 可省略自定义实现。

更新后的请求定义:

struct UserRequest: Requestable {
    var endpoint: String { "/users/\(userId)" }
    let userId: String
    // 无需 parse,默认使用扩展中的实现
}

struct ArticleRequest: Requestable {
    var endpoint: String { "/articles" }
    // 无需 parse,默认使用扩展中的实现
}

支持 mock 服务

为测试目的,添加一个模拟网络服务:

struct MockService: NetworkService {
    func fetch<T: Requestable>(_ request: T, completion: @escaping (Result<T.Response, Error>) -> Void) {
        if request.endpoint.contains("users") {
            let user = User(id: "001", name: "Alice")
            completion(.success(user as! T.Response))
        } else if request.endpoint == "/articles" {
            let articles = [Article(title: "Intro", content: "Hello")]
            completion(.success(articles as! T.Response))
        } else {
            completion(.failure(NetworkError.noData))
        }
    }
}

let mock = MockService()
mock.fetch(userRequest) { print($0) }      // 输出: success(User(id: "001", name: "Alice"))
mock.fetch(articleRequest) { print($0) }   // 输出: success([Article(title: "Intro", ...)])
  • MockService 模拟返回固定数据,便于单元测试。

案例分析:优势与设计

  1. 泛型与类型安全:T: Requestable 和 Response 确保请求和响应的类型匹配。
  2. 协议解耦:NetworkService 与具体实现分离,支持替换(如 URLSessionService 和 MockService)。
  3. 扩展复用:默认 parse 减少重复代码,同时保留自定义能力。
  4. 动态分派:通过协议类型调用 fetch,支持运行时多态。

注意事项

  • 错误处理:实际应用中需完善错误类型和重试机制。
  • 异步改进:可用 async/await 替代闭包,提升现代性(Swift 5.5+)。
  • 性能:泛型特化确保无运行时开销,但动态分派可能影响协议类型调用。

小结

通过这个泛型网络请求框架,我们展示了协议与泛型的强大结合。关联类型定义了灵活的响应类型,协议约束确保类型安全,扩展提供了复用逻辑。这一设计不仅适用于网络请求,还可扩展到其他领域(如数据库操作)。下一章将探讨协议在依赖注入和解耦中的应用,进一步提升代码质量。


Last Updated:: 3/10/25, 2:58 PM