具有并非总是使用的属性的Swift协议

时间:2016-09-17 19:02:31

标签: protocols swift3

这是对此问题的跟进:Can I have a Swift protocol without functions

假设我想在协议中添加更多属性:

protocol Nameable {
     var name: String {get}
     var fullName: String: {get}
     var nickName: String: {get}
}

但是,并非每个符合此协议的结构都可能具有fullName和/或nickName。我该怎么做?我可以以某种方式使这两个属性可选吗?或者我可能需要三个独立的协议?或者我只是将它们添加到每个结构中,但将它们留空,如下所示:

struct Person : Nameable {
    let name: String
    let fullName: String
    let nickName: String
    let age: Int
    // other properties of a Person
}

let person = Person(name: "Steve", fullName: "", nickName: "Stevie", age: 21)

编译和工作,但我不知道这是否是正确的'接近?

2 个答案:

答案 0 :(得分:3)

与Objective-C不同,您无法在纯Swift中定义可选协议要求。符合协议的类型必须采用指定的所有要求。

允许可选属性需求的一种可能方法是将它们定义为可选项,使用只返回nil的计算属性的默认实现。

protocol Nameable {
    var name : String? { get }
    var fullName : String? { get }
    var nickname : String? { get }
}

extension Nameable {
    var name : String? { return nil }
    var fullName : String? { return nil }
    var nickname : String? { return nil }
}

struct Person : Nameable {

    // Person now has the option not to implement the protocol requirements,
    // as they have default implementations that return nil

    // What's cool is you can implement the optional typed property requirements with
    // non-optional properties – as this doesn't break the contract with the protocol.
    var name : String
}

let p = Person(name: "Allan")
print(p.name) // Allan

然而,这种方法的缺点是,您可能会污染符合要求的类型以及他们不会实现的属性(在这种情况下为fullName& nickName)。

因此,如果一个类型具有这些属性没有逻辑意义(假设你想要City符合Nameable - 但是城市不会(真的)有昵称),那么你不应该将其符合Nameable

正如您所说,更灵活的解决方案是定义多个协议以定义这些要求。这样,类型可以准确地选择他们想要实现的要求。

protocol Nameable {
    var name : String { get }
}

protocol FullNameable {
    var fullName : String { get }
}

protocol NickNameable {
    // Even types that conform to NickNameable may have instances without nicknames.
    var nickname : String? { get }
}

// A City only needs a name, not a fullname or nickname
struct City : Nameable {
    var name : String
}

let london = City(name: "London")

// Person can have a name, full-name or nickname
struct Person : Nameable, FullNameable, NickNameable {
    var name : String
    var fullName: String
    var nickname: String?
}

let allan = Person(name: "Allan", fullName: "Allan Doe", nickname: "Al")

为了方便起见,您甚至可以使用protocol composition来定义typealias来表示所有这三个协议,例如:

typealias CombinedNameable = Nameable & FullNameable & NickNameable

struct Person : CombinedNameable {
    var name : String
    var fullName: String
    var nickname: String?
}

答案 1 :(得分:1)

您可以使用协议extension为这些属性提供默认实现,并覆盖classes/structs中实际需要的属性

extension Nameable{
   var fullName: String{
      return "NoFullName"
   }

  var nickName: String{
     return "NoNickName"
  }
}
struct Foo : Nameable{
   var name: String
}