В отличие от 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?
}
Я не был осведомлен о составе протокола, спасибо за обмен этой ссылке , Интересно, что Apple вызывает протокол в примере «Именованный», в то время как в инструкциях Swift API указано «Протоколы, описывающие возможность, следует называть с помощью суффиксов, способных, гибких или ing (например, Equatable, ProgressReporting)». – Koen
@Koen Happy to help :) Что касается руководящих принципов именования протоколов, то ИМО как «Именованные», так и «Инициативные» приемлемы, поскольку оба они описывают возможность совершенно напрямую. Обычно я думаю об этом, так как он должен соответствовать предложению «An X is Y», где X является соответствующим типом, а Y является протоколом (например, «Лицо является Именимым/Именованным»). Несмотря на мою собственную логику, «CombinedNameable» не является отличным выбором (я собирался назвать его «FullyNameable», но это было слишком близко к IMO «FullNameable»). – Hamish