通过观看Apple提供的视频教程,似乎swift是面向协议的编程语言,苹果鼓励程序员使用协议而不是类 . 但从我个人的角度来看,我认为协议没有明显的优势 . class可以符合协议,但它们也可以从超类继承 . 我们可以为协议添加扩展,但我们也可以为类添加扩展 . 我们可以在符合协议的类中实现函数,但我们也可以在子类中覆盖func . 我仍然感到困惑,为什么我们需要使用协议而不是类 . 当我们应该使用协议而不是类?
让我们来看一个下载示例 .
你有一个基类 FileDownloadModel 并有3个子类 AudioFileDownloadModel, VideoFileDownloadModel, ImageDownloadModel .
你有 DownloadManager ,输入为 FileDownloadModel 并使用其urlToDownload属性模型下载文件 .
后来你会被告知,还有一个模型会出现,但它的类似于 UserDownloadModel ,它是 User 的子类 .
现在看,很难处理这样的情况,你必须改变很多代码来合并下载方法 .
面向协议的编程如何帮助您:
创建名为 DownloadingFileProtocol 的协议,并添加下载文件所需的方法 . 例如 . urlToDownload,pathToSave,extension等 .
在 FileDownloadModel 和 UserDownloadModel 中实施相同的协议 . 查看 UserDownloadModel 中不需要更改大量代码的好处 . 您只需实现 DownloadingFileProtocol 中的方法 .
再看看新的实体是否再次出现,你不会改变任何代码只是实现协议方法 .
现在您的 DownloadManager 可以将输入视为 DownloadingFileProtocol 而不是特定型号,现在您可以将任何模型设为可下载 .
类和协议是正交概念 . 协议跨越类树并且使用不同的祖先连接一个或多个类 .
也许更简单地说:
"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:
CustomStringConvertible
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 }
这是可能的,因为声明一个类实现一个协议是一个承诺,我可以使用协议中的方法,知道它们已经实现并且(可能)做了预期的事情 .
很大程度上,它是类型的层次结构 . 假设您有一个表示 GlowingRedCube 的对象,但您希望在许多关心的通用代码中使用该类型:
GlowingRedCube
不同的形状 - Cube 延伸 Shape
Cube
Shape
不同颜色 - Red 延伸 Colorful
Red
Colorful
不同类型的照明 - Glowing 延伸 Illuminated
Glowing
Illuminated
你有麻烦了 . 你可以选择一个基类并添加特化: GlowingRedCube extends GlowingCube extends Shape ,但是你会得到一个非常广泛的类,以及一组不灵活的东西(如果你想制作一个 SoftRedCube ,但保留任何方法你会怎样为您现有的红色立方体类型定义?)
GlowingCube
SoftRedCube
你可能只有 Cube 并且具有属性的照明和形状,但是你没有得到好的编译器类型检查:如果你有一个 Room.lightUp() 方法并且必须传递它 Cube ,那么你需要检查该类型是否包含任何照明!如果你只能传递 Illuminated 那么编译器会在你尝试时立即阻止你 .
Room.lightUp()
协议允许您将其分开: GlowingRedCube 可以实现 Illuminated 协议, Colorful 协议和 Shape 协议 . 由于协议扩展,您可以包含功能的默认实现,因此您不必选择要将其附加到的层次结构级别 .
struct GlowingRedCube: Shape, Colorful, Illuminated { // .. }
实际上,协议允许您将行为附加到对象,而不管该对象做了什么 . 's exactly why they'用于委托和数据源协议之类的东西:即使你是大多数人将这些东西附加到 ViewController ,底层对象是不相关的,因此您可以灵活地了解如何实现 .
ViewController
从WWDC last year开始,有一个关于这种方法的精彩视频,但是首先要花一些时间自己尝试一些不同的对象结构以了解问题 .
使用协议,一个类/结构可以用作不同的东西 . 例如, String struct符合soooo许多协议!
String
Comparable CustomDebugStringConvertible Equatable ExtendedGraphemeClusterLiteralConvertible Hashable MirrorPathType OutputStreamType Streamable StringInterpolationConvertible StringLiteralConvertible UnicodeScalarLiteralConvertible
这意味着 String 可以用作11种不同的东西!当方法需要上述任何一个协议作为参数时,您可以传入一个字符串 .
“但我可以创建一个拥有协议所有方法的神级!”你争辩说 . 请记住,您只能从Swift中的一个类继承,并且多重继承使用起来非常危险,因为它可以使您的代码变得非常复杂 .
此外,使用协议,您可以定义类的自定义行为 . String 的 hashcode 方法与 Int 的方法不同 . 但它们都与此功能兼容:
hashcode
Int
func doStuff(x: Hashable) { }
这是协议的真正奇迹 .
最后但同样重要的是,协议是有道理的 . 协议代表"is a kind of"或"can be used as"关系 . 当X可以用作Y时,X符合协议Y是正确的,对吧?
4 回答
让我们来看一个下载示例 .
你有一个基类 FileDownloadModel 并有3个子类 AudioFileDownloadModel, VideoFileDownloadModel, ImageDownloadModel .
你有 DownloadManager ,输入为 FileDownloadModel 并使用其urlToDownload属性模型下载文件 .
后来你会被告知,还有一个模型会出现,但它的类似于 UserDownloadModel ,它是 User 的子类 .
现在看,很难处理这样的情况,你必须改变很多代码来合并下载方法 .
创建名为 DownloadingFileProtocol 的协议,并添加下载文件所需的方法 . 例如 . urlToDownload,pathToSave,extension等 .
在 FileDownloadModel 和 UserDownloadModel 中实施相同的协议 . 查看 UserDownloadModel 中不需要更改大量代码的好处 . 您只需实现 DownloadingFileProtocol 中的方法 .
再看看新的实体是否再次出现,你不会改变任何代码只是实现协议方法 .
现在您的 DownloadManager 可以将输入视为 DownloadingFileProtocol 而不是特定型号,现在您可以将任何模型设为可下载 .
类和协议是正交概念 . 协议跨越类树并且使用不同的祖先连接一个或多个类 .
也许更简单地说:
"class"定义了对象是什么 .
"protocol"定义了对象具有的行为 .
所以你有一个班车:
和一个颜色:
现在,或多或少显然颜色和汽车完全不相关,但是,假设我希望能够轻松地将其中一个转换为字符串,所以我可以调试:
要么
正是出于这个目的,Swift语言定义了协议
CustomStringConvertible
,因此我们可以声明可以使用该协议打印Car:还有一个颜色:
所以在我需要为每个类创建一个打印方法之前,现在我只需要一个看起来像这样的打印方法:
这是可能的,因为声明一个类实现一个协议是一个承诺,我可以使用协议中的方法,知道它们已经实现并且(可能)做了预期的事情 .
很大程度上,它是类型的层次结构 . 假设您有一个表示
GlowingRedCube
的对象,但您希望在许多关心的通用代码中使用该类型:不同的形状 -
Cube
延伸Shape
不同颜色 -
Red
延伸Colorful
不同类型的照明 -
Glowing
延伸Illuminated
你有麻烦了 . 你可以选择一个基类并添加特化:
GlowingRedCube
extendsGlowingCube
extendsShape
,但是你会得到一个非常广泛的类,以及一组不灵活的东西(如果你想制作一个SoftRedCube
,但保留任何方法你会怎样为您现有的红色立方体类型定义?)你可能只有
Cube
并且具有属性的照明和形状,但是你没有得到好的编译器类型检查:如果你有一个Room.lightUp()
方法并且必须传递它Cube
,那么你需要检查该类型是否包含任何照明!如果你只能传递Illuminated
那么编译器会在你尝试时立即阻止你 .协议允许您将其分开:
GlowingRedCube
可以实现Illuminated
协议,Colorful
协议和Shape
协议 . 由于协议扩展,您可以包含功能的默认实现,因此您不必选择要将其附加到的层次结构级别 .实际上,协议允许您将行为附加到对象,而不管该对象做了什么 . 's exactly why they'用于委托和数据源协议之类的东西:即使你是大多数人将这些东西附加到
ViewController
,底层对象是不相关的,因此您可以灵活地了解如何实现 .从WWDC last year开始,有一个关于这种方法的精彩视频,但是首先要花一些时间自己尝试一些不同的对象结构以了解问题 .
使用协议,一个类/结构可以用作不同的东西 . 例如,
String
struct符合soooo许多协议!这意味着
String
可以用作11种不同的东西!当方法需要上述任何一个协议作为参数时,您可以传入一个字符串 .“但我可以创建一个拥有协议所有方法的神级!”你争辩说 . 请记住,您只能从Swift中的一个类继承,并且多重继承使用起来非常危险,因为它可以使您的代码变得非常复杂 .
此外,使用协议,您可以定义类的自定义行为 .
String
的hashcode
方法与Int
的方法不同 . 但它们都与此功能兼容:这是协议的真正奇迹 .
最后但同样重要的是,协议是有道理的 . 协议代表"is a kind of"或"can be used as"关系 . 当X可以用作Y时,X符合协议Y是正确的,对吧?