首页 文章

为什么协议在swift中比class好? [关闭]

提问于
浏览
10

通过观看Apple提供的视频教程,似乎swift是面向协议的编程语言,苹果鼓励程序员使用协议而不是类 . 但从我个人的角度来看,我认为协议没有明显的优势 . class可以符合协议,但它们也可以从超类继承 . 我们可以为协议添加扩展,但我们也可以为类添加扩展 . 我们可以在符合协议的类中实现函数,但我们也可以在子类中覆盖func . 我仍然感到困惑,为什么我们需要使用协议而不是类 . 当我们应该使用协议而不是类?

4 回答

  • 12

    让我们来看一个下载示例 .

    你有一个基类 FileDownloadModel 并有3个子类 AudioFileDownloadModel, VideoFileDownloadModel, ImageDownloadModel .

    你有 DownloadManager ,输入为 FileDownloadModel 并使用其urlToDownload属性模型下载文件 .

    后来你会被告知,还有一个模型会出现,但它的类似于 UserDownloadModel ,它是 User 的子类 .

    现在看,很难处理这样的情况,你必须改变很多代码来合并下载方法 .

    面向协议的编程如何帮助您:

    • 创建名为 DownloadingFileProtocol 的协议,并添加下载文件所需的方法 . 例如 . urlToDownload,pathToSave,extension等 .

    • FileDownloadModelUserDownloadModel 中实施相同的协议 . 查看 UserDownloadModel 中不需要更改大量代码的好处 . 您只需实现 DownloadingFileProtocol 中的方法 .

    • 再看看新的实体是否再次出现,你不会改变任何代码只是实现协议方法 .

    • 现在您的 DownloadManager 可以将输入视为 DownloadingFileProtocol 而不是特定型号,现在您可以将任何模型设为可下载 .

  • 1

    类和协议是正交概念 . 协议跨越类树并且使用不同的祖先连接一个或多个类 .

    也许更简单地说:

    • "class"定义了对象是什么 .

    • "protocol"定义了对象具有的行为 .

    所以你有一个班车:

    class Car {
        var bodyStyle : String
    }
    

    和一个颜色:

    class Color {
        var red : Int
        var green : Int
        var blue : Int
    }
    

    现在,或多或少显然颜色和汽车完全不相关,但是,假设我希望能够轻松地将其中一个转换为字符串,所以我可以调试:

    print(Car(...))
    

    要么

    print(Color(...))
    

    正是出于这个目的,Swift语言定义了协议 CustomStringConvertible ,因此我们可以声明可以使用该协议打印Car:

    extension Car : CustomStringConvertible {
        var description : String { get { return "Car: \(bodyStyle)" } }
    }
    

    还有一个颜色:

    extension Color : CustomStringConvertible {
        var description : String { get { return "Color: \(red) \(green) \(blue)" } }
    }
    

    所以在我需要为每个类创建一个打印方法之前,现在我只需要一个看起来像这样的打印方法:

    func print(data:CustomStringConvertible) {
        let string = data.description
        ... bunch of code to actually print the line
    }
    

    这是可能的,因为声明一个类实现一个协议是一个承诺,我可以使用协议中的方法,知道它们已经实现并且(可能)做了预期的事情 .

  • 8

    很大程度上,它是类型的层次结构 . 假设您有一个表示 GlowingRedCube 的对象,但您希望在许多关心的通用代码中使用该类型:

    • 不同的形状 - Cube 延伸 Shape

    • 不同颜色 - Red 延伸 Colorful

    • 不同类型的照明 - Glowing 延伸 Illuminated

    你有麻烦了 . 你可以选择一个基类并添加特化: GlowingRedCube extends GlowingCube extends Shape ,但是你会得到一个非常广泛的类,以及一组不灵活的东西(如果你想制作一个 SoftRedCube ,但保留任何方法你会怎样为您现有的红色立方体类型定义?)

    你可能只有 Cube 并且具有属性的照明和形状,但是你没有得到好的编译器类型检查:如果你有一个 Room.lightUp() 方法并且必须传递它 Cube ,那么你需要检查该类型是否包含任何照明!如果你只能传递 Illuminated 那么编译器会在你尝试时立即阻止你 .

    协议允许您将其分开: GlowingRedCube 可以实现 Illuminated 协议, Colorful 协议和 Shape 协议 . 由于协议扩展,您可以包含功能的默认实现,因此您不必选择要将其附加到的层次结构级别 .

    struct GlowingRedCube: Shape, Colorful, Illuminated {
     // ..
    }
    

    实际上,协议允许您将行为附加到对象,而不管该对象做了什么 . 's exactly why they'用于委托和数据源协议之类的东西:即使你是大多数人将这些东西附加到 ViewController ,底层对象是不相关的,因此您可以灵活地了解如何实现 .

    WWDC last year开始,有一个关于这种方法的精彩视频,但是首先要花一些时间自己尝试一些不同的对象结构以了解问题 .

  • 3

    使用协议,一个类/结构可以用作不同的东西 . 例如, String struct符合soooo许多协议!

    Comparable
    CustomDebugStringConvertible
    Equatable
    ExtendedGraphemeClusterLiteralConvertible
    Hashable
    MirrorPathType
    OutputStreamType
    Streamable
    StringInterpolationConvertible
    StringLiteralConvertible
    UnicodeScalarLiteralConvertible
    

    这意味着 String 可以用作11种不同的东西!当方法需要上述任何一个协议作为参数时,您可以传入一个字符串 .

    “但我可以创建一个拥有协议所有方法的神级!”你争辩说 . 请记住,您只能从Swift中的一个类继承,并且多重继承使用起来非常危险,因为它可以使您的代码变得非常复杂 .

    此外,使用协议,您可以定义类的自定义行为 . Stringhashcode 方法与 Int 的方法不同 . 但它们都与此功能兼容:

    func doStuff(x: Hashable) {
    
    }
    

    这是协议的真正奇迹 .

    最后但同样重要的是,协议是有道理的 . 协议代表"is a kind of"或"can be used as"关系 . 当X可以用作Y时,X符合协议Y是正确的,对吧?

相关问题