Swift中什么是面向协议编程(POP)

62 阅读2分钟

面向协议编程 (Protocol-oriented Programming, POP) 是一种编程范式,由 Apple 在 Swift 中大力倡导。它的核心思想是优先使用协议和协议扩展来进行抽象和代码复用,而不是依赖于类的继承

核心理念

  1. 从协议开始设计:在设计系统时,首先考虑定义一组描述行为和能力的协议,而不是先设计一个基类。
  2. 利用协议扩展共享实现:通过协议扩展为这些行为提供默认实现。
  3. 用值类型和引用类型组合功能:让结构体、枚举和类通过遵循这些协议来获得所需的功能。

它解决了传统继承的一些痛点

  • 避免了深度和僵化的继承层级(“臃肿基类”问题)。
  • 让值类型(struct, enum)也能参与到代码复用的体系中。
  • 通过遵循多个协议,可以更灵活地组合功能。

示例: 假设我们要为游戏中的多个角色定义“攻击”和“防御”行为。

  1. 定义协议

    protocol Attackable {
        var attackPower: Int { get }
        func attack(target: Damageable)
    }
    ​
    protocol Damageable {
        var health: Int { get set }
        mutating func takeDamage(_ amount: Int)
    }
    
  2. 通过协议扩展提供默认实现

    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)")
        }
    }
    
  3. 创建具体类型并遵循协议

    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,所以它只能被攻击,但不能主动攻击。这就是面向协议编程的灵活性。