4-13.【协议导向编程】如何在 protocol 中提供默认实现,同时保证子类型可重写?

1 阅读1分钟

一、核心原则

只有在协议本体中声明的方法,conforming 类型才能重写 protocol extension 的默认实现。

  • 协议声明 = “多态契约”
  • extension 默认实现 = “默认行为”
  • conforming 类型实现 = “覆盖(override)默认行为”

二、最小示例

// 1️⃣ 协议声明多态方法
protocol Greeter {
    func greet()  // ✅ 协议声明,多态点
}

// 2️⃣ 提供默认实现
extension Greeter {
    func greet() {
        print("Hello from default implementation")
    }
}

// 3️⃣ 类型覆盖默认实现
struct Person: Greeter {
    func greet() {
        print("Hello from Person")
    }
}

// 4️⃣ 调用
let g: Greeter = Person()
g.greet()

输出:

Hello from Person

✅ 默认实现被覆盖,保证了多态。


三、为什么这样可行

  1. greet() 在协议中声明 → 进入 witness table
  2. extension 提供默认实现 → 如果类型没有提供自己的实现,就使用
  3. 类型提供实现 → 替换 witness table 中默认实现
  4. 通过协议类型调用 → 动态派发,最终调用 conforming 类型实现

四、工程级模板(可复用模式)

protocol Renderer {
    func draw()          // 多态点
}

extension Renderer {
    func draw() {        // 默认实现
        print("default draw")
    }

    func render() {      // 模板方法,可调用 draw()
        prepare()
        draw()
        finish()
    }

    func prepare() {
        print("prepare")
    }

    func finish() {
        print("finish")
    }
}

struct MetalRenderer: Renderer {
    func draw() {        // 覆盖默认实现
        print("Metal draw")
    }
}

let renderer: Renderer = MetalRenderer()
renderer.render()
// 输出:
// prepare
// Metal draw
// finish

特点:

  • draw() 是多态点 → conforming 类型可覆盖
  • render() 是模板方法 → 静态派发,默认行为可复用
  • 组合 extension + 协议声明 = 既可复用,又可重写

五、设计口诀

协议声明 = 多态契约
协议扩展默认实现 = 工具或默认行为
conforming 类型实现 = 覆盖默认实现(动态派发)