为什么协议在swift中比class好?

时间:2016-03-22 04:16:36

标签: ios swift class protocols

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

4 个答案:

答案 0 :(得分:13)

类和协议是正交概念。协议跨越类树,并使用不同的祖先加入一个或多个类。

或许更简单地说:

  • "类"定义对象是什么。
  • "协议"定义对象具有的行为。

所以你有一个班车:

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
}

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

答案 1 :(得分:8)

让我们下载一个例子。

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

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

稍后您会被告知还有一个模型即将推出,但它的类型为 UserDownloadModel ,它是用户<的子类 / strong>,而不是 FileDownloadModel

现在看,很难处理这种情况,你必须更改大量代码以合并下载方法。

  

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

  1. 创建名为 DownloadingFileProtocol 的协议并添加 下载文件所需的方法和属性。例如。 urlToDownload, pathToSave,extension等。
  2. FileDownloadModel 中实施相同的协议 的 UserDownloadModel 即可。这样做的好处是你不必改变一个 UserDownloadModel 中的大量代码。你将实现 来自 DownloadingFileProtocol
  3. 的方法
  4. 如果新实体再次出现,那么你就不会 改变任何代码。相反,您只需实施协议方法。
  5. 现在您的 DownloadManager 可以使用 DownloadingFileProtocol 作为输入而不是特定型号。此外,您现在可以制作任何模型&#34;可下载&#34;通过它采用这个协议。

答案 2 :(得分: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的这种方法有一个很棒的视频,但是首先要花一些时间自己尝试一些不同的对象结构以了解问题。

答案 3 :(得分:1)

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

Comparable
CustomDebugStringConvertible
Equatable
ExtendedGraphemeClusterLiteralConvertible
Hashable
MirrorPathType
OutputStreamType
Streamable
StringInterpolationConvertible
StringLiteralConvertible
UnicodeScalarLiteralConvertible

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

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

此外,使用协议,您可以定义类的自定义行为。 String的{​​{1}}方法与hashcode方法不同。但它们都与此功能兼容:

Int

这是协议的真正奇迹。

最后但并非最不重要的是,协议有意义。协议代表“是一种”或“可以用作”的关系。当X可以用作Y时,X符合协议Y是对的吗,对吗?