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

第3章:协议扩展(Protocol Extensions)

3.2 为协议添加默认实现

协议扩展的一个主要用途是为协议中的方法和属性提供默认实现,从而减少遵循者的代码重复并提高开发效率。通过默认实现,协议不仅定义了“做什么”,还可以在一定程度上规定“如何做”,让遵循者可以选择直接使用默认逻辑或根据需要自定义实现。本节将深入探讨如何为协议添加默认实现,分析其设计原则,并通过示例展示其在实际开发中的应用。

默认实现的意义

在没有协议扩展的情况下,遵循协议的类型必须为每个要求提供具体实现。例如:

protocol Reportable {
    var summary: String { get }
    func generateReport() -> String
}

struct Task: Reportable {
    var summary: String
    
    func generateReport() -> String {
        return "Task Report: \(summary)"
    }
}

struct Project: Reportable {
    var summary: String
    
    func generateReport() -> String {
        return "Project Report: \(summary)"
    }
}

在这个例子中,Task 和 Project 的 generateReport() 实现虽然略有不同,但核心逻辑都是基于 summary 生成报告。如果有更多类型遵循 Reportable,这种重复代码会迅速增加。而通过协议扩展,我们可以提供默认实现来解决这个问题。

添加默认实现的语法

为协议添加默认实现只需在 extension 中实现协议要求的方法或属性。例如:

extension Reportable {
    func generateReport() -> String {
        return "Report: \(summary)"
    }
}

struct Task: Reportable {
    var summary: String
}

struct Project: Reportable {
    var summary: String
}

let task = Task(summary: "Complete coding")
let project = Project(summary: "Launch app")
print(task.generateReport())    // 输出: Report: Complete coding
print(project.generateReport()) // 输出: Report: Launch app
  • generateReport() 的默认实现直接使用协议要求的 summary 属性。
  • Task 和 Project 无需自己实现 generateReport(),直接复用扩展中的逻辑。

覆盖默认实现

遵循者可以选择覆盖默认实现,以满足特定需求。例如:

struct DetailedTask: Reportable {
    var summary: String
    
    func generateReport() -> String {
        return "Detailed Task Report: \(summary) [Priority: High]"
    }
}

let detailedTask = DetailedTask(summary: "Fix bugs")
print(detailedTask.generateReport()) // 输出: Detailed Task Report: Fix bugs [Priority: High]

在这里,DetailedTask 覆盖了默认实现,添加了额外的优先级信息。这种灵活性让默认实现既方便又不失定制能力。

为属性添加默认实现

协议扩展也可以为计算属性提供默认实现。例如:

protocol Measurable {
    var length: Double { get }
    var lengthDescription: String { get }
}

extension Measurable {
    var lengthDescription: String {
        return "\(length) units"
    }
}

struct Rope: Measurable {
    var length: Double
}

let rope = Rope(length: 5.5)
print(rope.lengthDescription) // 输出: 5.5 units
  • lengthDescription 的默认实现基于 length,无需每个遵循者重复定义。

如果需要更复杂的逻辑,遵循者仍可覆盖:

struct MetricRope: Measurable {
    var length: Double
    
    var lengthDescription: String {
        return "\(length) meters"
    }
}

默认实现的实际应用

让我们通过一个更贴近实际的案例展示默认实现的威力。假设我们要设计一个日志系统:

protocol Loggable {
    var logLevel: Int { get }
    func log(_ message: String)
}

extension Loggable {
    func log(_ message: String) {
        let prefix = logLevel > 1 ? "[DEBUG]" : "[INFO]"
        print("\(prefix) Level \(logLevel): \(message)")
    }
}

struct ConsoleLogger: Loggable {
    var logLevel: Int
}

struct FileLogger: Loggable {
    var logLevel: Int
    
    func log(_ message: String) {
        // 自定义实现:模拟写入文件
        print("[FILE] Level \(logLevel): \(message)")
    }
}

let console = ConsoleLogger(logLevel: 2)
console.log("App started") // 输出: [DEBUG] Level 2: App started

let file = FileLogger(logLevel: 1)
file.log("App started")    // 输出: [FILE] Level 1: App started
  • ConsoleLogger 使用默认实现,基于日志级别动态添加前缀。
  • FileLogger 覆盖默认实现,模拟文件日志的特殊格式。

设计默认实现的原则

  1. 通用性:默认实现应尽量满足大多数遵循者的需求,避免过于特定。
    • 例如,log(_:) 的默认实现使用通用前缀,而不是特定于某一场景。
  2. 简洁性:避免在默认实现中引入复杂逻辑,以免增加理解和维护成本。
  3. 可覆盖性:设计时考虑遵循者可能需要定制,确保默认实现易于被替换。
  4. 依赖最小化:默认实现应只依赖协议本身的要求,避免引入外部假设。

注意事项

  • 不能强制使用:默认实现是可选的,遵循者可以忽略或覆盖它。
  • 分派机制:默认实现的调用可能是静态分派(值类型)或动态分派(类),影响性能。
  • 无存储属性:扩展中只能定义计算属性或方法,无法添加存储属性。
    • 解决方法:通过协议要求提供必要的数据接口。

小结

为协议添加默认实现是通过协议扩展提升代码复用性的核心手段。它允许开发者在协议中集中通用逻辑,同时保留遵循者的灵活性。通过合理的默认实现设计,我们可以减少样板代码、提高一致性并简化开发流程。下一节将介绍如何使用 where 子句约束扩展,进一步增强协议扩展的精确性和功能性。


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