395,Swift中的enum的协议(面试点:可以添加计算属性,不可以添加存储属性,mutating 可以改变自身枚举值,可以为枚举扩展extension,可以使用关联值的方法,来实现计算属性:...

协议(Protocols)

我已经提及了structsenums之间的相似性。除了附加方法的能力之外,Swift也允许你在枚举中使用协议(Protocols)协议扩展(Protocol Extension)

Swift协议定义一个接口或类型以供其他数据结构来遵循。enum当然也不例外。我们先从Swift标准库中的一个例子开始.

CustomStringConvertible是一个以打印为目的的自定义格式化输出的类型。

protocol CustomStringConvertible {
  var description: String { get }
}

该协议只有一个要求,即一个只读(getter)类型的字符串(String类型)。我们可以很容易为enum实现这个协议。

一些协议的实现可能需要根据内部状态来相应处理要求。例如定义一个管理银行账号的协议。

protocol AccountCompatible {
  var remainingFunds: Int { get }
  mutating func addFunds(amount: Int) throws
  mutating func removeFunds(amount: Int) throws
}

你也许会简单地拿struct实现这个协议,但是考虑应用的上下文,enum是一个更明智的处理方法。不过你无法添加一个存储属性到enum中,就像var remainingFuns:Int。那么你会如何构造呢?答案灰常简单,你可以使用关联值完美解决:

enum Account {
  case Empty
  case Funds(remaining: Int)

  enum Error: ErrorType {
    case Overdraft(amount: Int)
  }

  var remainingFunds: Int {
    switch self {
    case Empty: return 0
    case Funds(let remaining): return remaining
    }
  }
}

为了保持代码清爽,我们可以在enum的协议扩展(protocl extension)中定义必须的协议函数:

extension Account: AccountCompatible {

  mutating func addFunds(amount: Int) throws {
    var newAmount = amount
    if case let .Funds(remaining) = self {
      newAmount += remaining
    }
    if newAmount < 0 {
      throw Error.Overdraft(amount: -newAmount)
    } else if newAmount == 0 {
      self = .Empty
    } else {
      self = .Funds(remaining: newAmount)
    }
  }

  mutating func removeFunds(amount: Int) throws {
    try self.addFunds(amount * -1)
  }

}
var account = Account.Funds(remaining: 20)
print("add: ", try? account.addFunds(10))
print ("remove 1: ", try? account.removeFunds(15))
print ("remove 2: ", try? account.removeFunds(55))
// prints:
// : add:  Optional(())
// : remove 1:  Optional(())
// : remove 2:  nil

正如你所看见的,我们通过将值存储到enum cases中实现了协议所有要求项。如此做法还有一个妙不可言的地方:现在整个代码基础上你只需要一个模式匹配就能测试空账号输入的情况。你不需要关心剩余资金是否等于零。

你可能感兴趣的:(395,Swift中的enum的协议(面试点:可以添加计算属性,不可以添加存储属性,mutating 可以改变自身枚举值,可以为枚举扩展extension,可以使用关联值的方法,来实现计算属性:...)