4-25.【协议导向编程】如何在大型项目中管理协议数量,避免“协议爆炸”?

0 阅读2分钟

一、协议爆炸的根源

  1. 每增加一个小功能就创建一个协议

    • 结果:几十、上百个小协议 → 难以追踪
  2. 协议职责不清

    • 一个协议承载多个能力 → 后续扩展难以组合
  3. 缺乏层次化 / 模块化

    • 协议散落在全局 → 难以归类与复用

二、设计原则

  1. 单一职责原则(SRP)

    • 一个协议只抽象一种能力
    • 避免“巨型协议”承载多个行为
  2. 模块化 + 命名空间

    • 协议按模块或功能域组织
    • 命名清晰,例如:Networking.RequestableUI.Refreshable
  3. 组合优先而非继承

    • 小协议组合 → 避免多继承或深层继承树
    • 通过 typealias 或协议组合形成复合能力
  4. 默认实现 / 模板方法复用

    • 减少每个协议都需要 conforming 类型实现重复逻辑
  5. 协议分层

    • 基础协议:核心能力
    • 高阶协议:组合多个基础能力
    • Extension 提供默认实现

三、工程实践策略

1️⃣ 协议分层设计

// 基础协议
protocol Flyable { func fly() }
protocol Swimmable { func swim() }

// 高阶协议组合
protocol SuperAnimal: Flyable, Swimmable {}

// 默认实现
extension Flyable { func fly() { print("Flying") } }
extension Swimmable { func swim() { print("Swimming") } }
  • 小协议 → 单一能力
  • 高阶协议 → 复合能力 → 避免重复创建新协议

2️⃣ 命名空间 + 模块化

enum Networking {
    protocol Requestable { func request() }
}

enum UI {
    protocol Refreshable { func refreshUI() }
}
  • 避免协议全局污染
  • 便于查找和维护

3️⃣ 使用 typealias 组合协议

protocol Drawable { func draw() }
protocol Animatable { func animate() }

typealias DrawableAnimatable = Drawable & Animatable

struct Sprite: DrawableAnimatable {
    func draw() { print("Draw") }
    func animate() { print("Animate") }
}
  • 避免为每种组合创建新协议
  • 保持协议数量可控

4️⃣ 默认实现 / 模板方法复用

protocol Logger { func log(message: String) }

extension Logger {
    func log(message: String) { print("Log: (message)") }
}

struct FileLogger: Logger {}
struct ConsoleLogger: Logger {}
  • 所有类型直接复用默认实现 → 避免每个类型都实现重复方法
  • 减少“重复协议 + 方法”带来的膨胀

5️⃣ 工程管理策略

策略说明
单一职责一个协议只关注一种能力
模块化 / 命名空间按功能域管理协议
小协议 + 高阶组合避免重复创建协议
默认实现 / 模板方法提供复用逻辑,减少 conforming 重复
typealias 组合协议减少组合协议数量

四、设计口诀

“小协议单一能力 → 高阶组合复用;
模块化管理命名 → 避免全局膨胀;
默认实现复用 → 减少重复;
typealias 组合 → 控制协议数量。”