【问题标题】:Swift protocol with properties that are not always used具有并不总是使用的属性的 Swift 协议
【发布时间】:2016-09-17 19:02:31
【问题描述】:

这是这个问题的后续: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)

这样可以编译并且可以工作,但我不知道这是否是“正确”的方法?

【问题讨论】:

    标签: protocols swift3


    【解决方案1】:

    与 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
    

    但是,这种方法的缺点是您可能会使用它们未实现的属性(在这种情况下为fullNamenickName)污染符合类型的类型。

    因此,如果一个类型具有这些属性在逻辑上没有意义(假设您想将 CityNameable 一致——但城市(实际上)没有昵称),那么您不应该遵循它到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 将示例中的协议称为 Named,而 Swift API 指南则声明为 Protocols that describe a capability should be named using the suffixes able, ible, or ing (e.g. Equatable, ProgressReporting).
    • @Koen 乐于助人 :) 关于协议命名准则,IMO NamedNameable 都是可以接受的,因为它们都非常直接地描述了一项功能。我通常认为它应该适合句子“An X is Y”,其中 X 是符合类型,Y 是协议(例如“A Person is Nameable/Named”)。虽然按照我自己的逻辑,CombinedNameable 不是一个好选择(我本来打算叫它FullyNameable,但这太接近FullNameable IMO)。
    【解决方案2】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-10-26
      • 2023-04-06
      • 1970-01-01
      • 1970-01-01
      • 2017-02-06
      • 1970-01-01
      • 1970-01-01
      • 2018-05-14
      相关资源
      最近更新 更多