面向协议编程 (Protocol-oriented Programming, POP) 是一种编程范式,由 Apple 在 Swift 中大力倡导。它的核心思想是优先使用协议和协议扩展来进行抽象和代码复用,而不是依赖于类的继承。
核心理念:
- 从协议开始设计:在设计系统时,首先考虑定义一组描述行为和能力的协议,而不是先设计一个基类。
- 利用协议扩展共享实现:通过协议扩展为这些行为提供默认实现。
- 用值类型和引用类型组合功能:让结构体、枚举和类通过遵循这些协议来获得所需的功能。
它解决了传统继承的一些痛点:
- 避免了深度和僵化的继承层级(“臃肿基类”问题)。
- 让值类型(struct, enum)也能参与到代码复用的体系中。
- 通过遵循多个协议,可以更灵活地组合功能。
示例: 假设我们要为游戏中的多个角色定义“攻击”和“防御”行为。
-
定义协议:
protocol Attackable { var attackPower: Int { get } func attack(target: Damageable) } protocol Damageable { var health: Int { get set } mutating func takeDamage(_ amount: Int) } -
通过协议扩展提供默认实现:
extension Attackable { func attack(target: Damageable) { var target = target // 需要一个可变的副本 print("造成 (attackPower) 点伤害!") target.takeDamage(attackPower) } } // mutating 关键字允许在方法内部修改自身的值 extension Damageable { mutating func takeDamage(_ amount: Int) { health -= amount print("受到 (amount) 点伤害,剩余生命值: (health)") } } -
创建具体类型并遵循协议:
struct Hero: Attackable, Damageable { let attackPower: Int = 20 var health: Int = 100 } struct Monster: Attackable, Damageable { let attackPower: Int = 15 var health: Int = 80 } struct TreasureChest: Damageable { var health: Int = 50 // 一个可以被“攻击”打开的宝箱 }在这个例子中,Hero 和 Monster 自动获得了 attack 和 takeDamage 的能力,无需编写任何重复代码。TreasureChest 只遵循了 Damageable,所以它只能被攻击,但不能主动攻击。这就是面向协议编程的灵活性。