这是对此问题的跟进: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)
编译和工作,但我不知道这是否是正确的'接近?
答案 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
}