通过观看苹果提供的视频教程,似乎Swift是面向协议编程语言,苹果鼓励程序员使用协议而非类。
但就我个人看来,我没有看到协议的明显优势。类可以符合协议,但它们也可以继承自超类。我们可以为协议添加扩展,但我们也可以为类添加扩展。我们可以在符合协议的类中实现函数,但我们也可以在子类中覆盖函数。
我仍然困惑为什么我们需要使用协议而不是类。以及何时应该使用协议而不是类?
类和协议是不同的概念。协议跨越类树,将一个或多个具有不同继承关系的类连接起来。
更简单地说:
因此,你可以有一个名为 Car 的类:
class Car {
var bodyStyle : String
}
和一个名为Color的类:
class Color {
var red : Int
var green : Int
var blue : Int
}
现在,颜色和汽车似乎完全无关,但是,假设我想轻松将它们中的任何一个转换为字符串,以便进行调试:
print(Car(...))
print(Color(...))
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
}
这是可能的,因为声明一个类实现协议就是承诺我可以使用该协议的方法,知道它们已经被实现并且(大概)做了期望的事情。
让我们以一个下载的例子来说明。
你有一个基类FileDownloadModel,并且有3个子类AudioFileDownloadModel、VideoFileDownloadModel和ImageDownloadModel。
你有一个DownloadManager,它接收FileDownloadModel作为输入,并使用该模型的urlToDownload属性来下载文件。
后来你被告知还有一种类型的模型即将到来,但它是UserDownloadModel,是User的一个子类,而不是FileDownloadModel。
现在这种情况变得很难处理,因为你需要改变很多代码来实现下载方法。
协议导向编程如何帮助你:
struct GlowingRedCube: Shape, Colorful, Illuminated {
// ..
}
ViewController
, 底层对象也不相关,因此你可以灵活地实现。通过协议,一个类/结构体可以被用作不同的事情。例如,String
结构体符合很多协议!
Comparable
CustomDebugStringConvertible
Equatable
ExtendedGraphemeClusterLiteralConvertible
Hashable
MirrorPathType
OutputStreamType
Streamable
StringInterpolationConvertible
StringLiteralConvertible
UnicodeScalarLiteralConvertible
String
可以用作11种不同的东西!当一个方法需要以上任何一种协议作为参数时,您可以传递一个字符串。String
的hashcode
方法与Int
的方法不同。但它们都与此函数兼容:func doStuff(x: Hashable) {
}
这就是协议的真正奇妙之处。
最后但并非最不重要的,协议有意义。协议代表了一种“是一种”或“可以用作”的关系。当X可以用作Y时,X符合协议Y是有意义的,对吧?
Vehicle
继承而来的Car
和一个从BuildingMaterial
继承而来的Wood
,并且我们想要为Car
和Wood
添加burn
方法,那么协议将是最合适的选择。 - tktsubota