Swift中面向协议的编程简介

当我几年前开始编码时,它全都与面向对象编程有关。 借助Swift,出现了一种新方法,使代码更易于重用和测试, 面向协议的编程

在介绍解决方案之前介绍问题,让我们假设我们拥有这个Vehicle类,并且Car继承了它的速度值。

class Vehicle { 
var speed : Double = 0.0
}
class Vehicle {
var speed : Double = 0.0
}
class Car : Vehicle {
override init() {
super.init()
self.speed = 90.0
}
}
class Car : Vehicle {
override init() {
super.init()
self.speed = 90.0
}
}

但是,在开车之前,我们使用了其他交通工具,例如马,也有特定的速度。 在没有继承的情况下,该新对象如何表现为其他对象? 这就是面向协议编程的亮点

面向协议的编程的主要好处在于使无关的对象符合相同的行为。 当OOP仅限于类时,它也可以应用于不同类型的对象,例如类,枚举或结构

回到我们的运输工具示例,我们可以定义一个Transporter协议,为我们的速度定义接口。 我还将切换到struct,因为我们将不再使用类继承。

 protocol Transporter { 
var speed : Double { get }
}
protocol Transporter {
var speed : Double { get }
}
struct Car : Transporter {
var speed: Double = 80
}
struct Car : Transporter {
var speed: Double = 80
}
struct Car : Transporter {
var speed: Double = 80
}
struct Horse : Transporter {
var speed: Double = 40
}
struct Horse : Transporter {
var speed: Double = 40
}
let car = Car()
let horse = Horse()
let transporters : [Transporter] = [car, horse]
let car = Car()
let horse = Horse()
let transporters : [Transporter] = [car, horse]
let car = Car()
let horse = Horse()
let transporters : [Transporter] = [car, horse]
transporters.forEach({ print("\($0.speed)") })

现在,马是一种动物,当汽车主要需要汽油来保持行驶时,需要休息一下,这也是我们可以用更多协议来描述的东西,因为它不仅限于我们可以实现的协议数量。

 protocol Motorised { 
var tankCapacity : Int { get }
}
protocol Motorised {
var tankCapacity : Int { get }
}
struct Car : Transporter, Motorised {
var speed: Double = 80
var tankCapacity: Int = 60
}
struct Car : Transporter, Motorised {
var speed: Double = 80
var tankCapacity: Int = 60
}
struct Car : Transporter, Motorised {
var speed: Double = 80
var tankCapacity: Int = 60
}
protocol Animal {
var restTime : Int { get }
}
protocol Animal {
var restTime : Int { get }
}
protocol Animal {
var restTime : Int { get }
}
struct Horse : Transporter, Animal {
var speed: Double = 40 var restTime: Int = 2
}
struct Horse : Transporter, Animal {
var speed: Double = 40 var restTime: Int = 2
}

那是最好的部分,明天如果我们有一个新的机动动物,我们可以很容易地实现一个对象来表示它。

面向协议编程的另一个优点是能够通过扩展协议方法来实现默认行为 。 如何知道我的一只动物是否可以默认运输?

 protocol Animal { 
var restTime : Int { get }
var canTransport : Bool { get }
}
protocol Animal {
var restTime : Int { get }
var canTransport : Bool { get }
}
extension Animal {
var canTransport : Bool {
return self is Transporter
}
}
extension Animal {
var canTransport : Bool {
return self is Transporter
}
}

很好的例子,但是在真正的iOS应用程序中呢?

公平地说,当您从网络请求中解析JSON对象时,将是一个具体示例。 默认情况下,并非所有对象都是可解析的,只有您需要的对象。 那就是面向协议的编程将再次变得很棒的地方。

 protocol LoggerProtocol { 
func log(event: Events, parameters: [String: Any]?)
}
protocol LoggerProtocol {
func log(event: Events, parameters: [String: Any]?)
}
class LoggerManager {
private var loggers : [LoggerProtocol] = []

func log(event: Events, parameters: [String : Any]? = nil) {
self.loggers.forEach({ $0.log(event: event, parameters: parameters) })
}
}
class LoggerManager {
private var loggers : [LoggerProtocol] = []

func log(event: Events, parameters: [String : Any]? = nil) {
self.loggers.forEach({ $0.log(event: event, parameters: parameters) })
}
}

快完成了 我们有一个基于协议的记录器帮助程序类来执行所有逻辑。 易于重用和测试。

面向协议编程的优点是使它们更易于测试。 您不需要为单元测试请求真正的服务器,实现正确协议的模型服务将获得相同的结果。 如果您想更多地了解这一部分,我最近介绍了一种在面向协议的编程之后用Swift编写单元测试的方法。

总之 ,我们已经看到,在Swift中面向协议的编程是一种出色的设计模式,可以使无关的对象符合相同的行为,无论是类,枚举还是结构。 我们还发现了如何实现默认实现,以及如何使我们的代码更易于重用和测试。


最初发布在 http://benoitpasquier.com/introduction-protocol-取向-programming-swift /中