【问题标题】:Why protocol is better than class in swift? [closed]为什么协议比swift中的类更好? [关闭]
【发布时间】:2016-03-22 04:16:35
【问题描述】:

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

【问题讨论】:

  • 这是我的示例:如果我们有继承自 VehicleCar 和继承自 BuildingMaterialWood,并且我们希望将方法 burn 添加到 CarWood,那么协议将是最合适的。
  • 我不确定您指的是哪个视频,但 WWDC 2015 的 Protocol-Oriented Programming 详细描述了协议的优点。

标签: ios swift class protocols


【解决方案1】:

类和协议是正交的概念。一个协议跨越类树并将一个或多个具有不同祖先的类连接起来。

也许更简单地说:

  • “类”定义对象是什么。
  • “协议”定义了对象的行为。

所以你有一个类 Car:

class Car {
    var bodyStyle : String
}

还有一个颜色类:

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

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

print(Car(...))

print(Color(...))

为此,Swift 语言定义了协议CustomStringConvertible,因此我们可以声明a 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
}

这是可能的,因为声明一个类实现了一个协议是一个承诺,即我可以使用协议中的方法,知道它们已实现并且(可能)执行预期的操作。

【讨论】:

  • 但你不是也能遵守 ObjC 中的多种协议吗? Swift 有什么不同?
【解决方案2】:

让我们以下载为例。

您有一个基类 FileDownloadModel,并有 3 个子类 AudioFileDownloadModel、VideoFileDownloadModel 和 ImageDownloadModel

您有一个 DownloadManager,它接受 FileDownloadModel 输入并使用此模型的 urlToDownload 属性下载文件。

稍后您会被告知还有一个模型即将推出,但它是 UserDownloadModel 类型,它是 User子类,而不是 FileDownloadModel

现在看来,处理这种情况变得很困难,您必须更改大量代码来合并下载方法。

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

  1. 创建一个名为 DownloadingFileProtocol 的协议并添加 下载文件所需的方法和属性。例如。 urlToDownload, pathToSave、扩展等。
  2. FileDownloadModel 中实现相同的协议和 用户下载模型。这里的好处是您不必更改 UserDownloadModel 中有很多代码。您只需实施 DownloadingFileProtocol 中的方法。
  3. 如果新实体再次出现,您将不会 更改任何代码。相反,您只需实现协议方法。
  4. 现在您的 DownloadManager 可以使用 DownloadingFileProtocol 作为输入而不是特定模型。此外,您现在可以通过采用此协议使任何模型“可下载”。

【讨论】:

    【解决方案3】:

    很大程度上,它是类型的层次结构。假设您有一个表示 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 {
     // ..
    }
    

    实际上,协议允许您将行为附加到对象上,而不管该对象还做了什么。这正是它们被用于委托和数据源协议之类的东西的原因:即使您主要将这些东西附加到ViewController,底层对象也不相关,因此您可以灵活地选择如何实现。

    在 Swift 中使用协议不仅仅是基础知识:它们异常强大,因为它们可以附加到许多不同的代码结构:类、结构和枚举。这使您可以真正首先处理编程协议。 WWDC last year 有一个关于这种方法的精彩视频,但它有助于先花一些时间自己尝试一些不同的对象结构以了解问题。

    【讨论】:

      【解决方案4】:

      使用协议,一个类/结构可以用作不同的东西。例如,String 结构体符合太多协议!

      Comparable
      CustomDebugStringConvertible
      Equatable
      ExtendedGraphemeClusterLiteralConvertible
      Hashable
      MirrorPathType
      OutputStreamType
      Streamable
      StringInterpolationConvertible
      StringLiteralConvertible
      UnicodeScalarLiteralConvertible
      

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

      “但是我可以创建一个包含协议所有方法的上帝类!”你争论。请记住,您只能从 Swift 中的一个类继承,而使用多重继承非常危险,它会使您的代码变得超级复杂。

      此外,通过协议,您可以定义类的自定义行为。 Stringhashcode 方法与Int 的方法不一样。但它们都与此功能兼容:

      func doStuff(x: Hashable) {
      
      }
      

      这是协议的真正奇迹。

      最后但同样重要的是,协议有意义。协议代表一种“是一种”或“可以用作”的关系。当 X 可以用作 Y 时,X 符合 Y 协议是有道理的,对吧?

      【讨论】:

        猜你喜欢
        • 2014-03-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-08-04
        • 1970-01-01
        • 2010-09-11
        • 1970-01-01
        相关资源
        最近更新 更多