2016-09-17 2 views
1

Это наблюдение на этот вопрос: Can I have a Swift protocol without functionsSwift протокол со свойствами, которые не всегда используются

Предположим, что я хочу, чтобы добавить дополнительные свойства к моему протоколу:

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

В отличие от 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? 
} 
+0

Я не был осведомлен о составе протокола, спасибо за обмен этой ссылке , Интересно, что Apple вызывает протокол в примере «Именованный», в то время как в инструкциях Swift API указано «Протоколы, описывающие возможность, следует называть с помощью суффиксов, способных, гибких или ing (например, Equatable, ProgressReporting)». – Koen

+0

@Koen Happy to help :) Что касается руководящих принципов именования протоколов, то ИМО как «Именованные», так и «Инициативные» приемлемы, поскольку оба они описывают возможность совершенно напрямую. Обычно я думаю об этом, так как он должен соответствовать предложению «An X is Y», где X является соответствующим типом, а Y является протоколом (например, «Лицо является Именимым/Именованным»). Несмотря на мою собственную логику, «CombinedNameable» не является отличным выбором (я собирался назвать его «FullyNameable», но это было слишком близко к IMO «FullNameable»). – Hamish

1

Вы можете дать реализацию по умолчанию для этого имущества с использованием протокола extension и переопределить свойство в classes/structs, где на самом деле вам необходимо

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

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