Swift的面向协议开发啥意思?有啥好处呢?

官方解释

面向协议编程,全称Protocol Oriented Programming,简称POP, 是 Apple 在 WWDC2015 上提出的一种编程范式,其已成为 Swift 的基础库。

在讲面向协议之前,介绍下协议的概念。

关于协议的概念,在苹果的官网是如下定义的:“协议定义了适合特定任务或功能的方法、属性和其他需求的蓝图。然后,类、结构体或枚举可以遵循该协议来提供这些需求的实际实现。任何满足协议要求的类型都被称为遵循该协议。”见 Swift 编程语言(Swift 4.0.3)部分。

这个比较难理解。我就结合个人经验,从几个方面理解下:

协议即Protocol,类似Java语言中Interface(接口),用于模块间通讯。但是又不完全类似接口。

协议在Object-C中使用的比较多,一般是结合delegate(委托),实现一个VC对另一个VC传递数据或响应事件。在此场景中的协议很像接口,定义好的一个规范。

和继承的关系,在Object-C和Swift语言中,我们知道是不支持多重继承的,然而可以通过协议来实现多重继承。

协议与多态,本来与多态扯不上关系,但是Swift4.0.3版本之后的协议扩展(Protocol extension)又可以实现多态功能。

大白话解释

常规的开发模式是面向对象开发,即:万物皆对象~ 任何一个Class的对象,都可以用很多属性。

假如我们现在有两个类:DogCar,这两个类中各有一个方法run()

考虑到封装和继承,我们可以为这两个类抽取一个父类,然后将run()方法放于父类中.

面向对象是一种不错的抽象方式,但是肯定不是最好的方式。因为汽车根本不是同一类事物,它无法描述两个不同事物(狗和汽车)具有某个相同特性(跑)这一点的要求。其实利用一些特性的组合要比继承更让人接受一些。

定义一个协议

//如果只希望协议被类遵守可以在协议后面加上 :class
protocol Runable : class{
/*
 *协议中既可以定义属性也可以定义方法
 *注意:
     1.协议中的属性和方法都不能有默认实现
     2.在定义属性时,必须明确的指出该属性是一个可读可写/只读/只写
     3.默认情况下protocol中的属性,必须被遵守协议的类/结构体实现
 */
    var speed : Int {get}
    func run()
}

如果我们希望像在OC里面一样,声明一个可选属性的协议可以这样来声明

/*
 *条件:
 1.必须在protocol的前面加上@objc
 2.在方法或者是属性的前面加上@objc + optional
 */
@objc protocol Runable : class{
   @objc optional var speed : Int {get}
   @objc optional func run()
}

假如类本身没有提供协议的实现,那么通过协议扩展提供的默认实现会被调用。

protocol Runable : class{}

//默认实现条件:必须在协议的extension中实现
extension Runable
{
    var speed : Int
    {
        return 20
        
    }
    func func()
    {
        print("正在奔跑中🏃🏻‍♀️...")
    }
}

使用场景

此时,产品经理需要我们实现一个点赞抖动的效果。如果按照面向对象的方式,我们有三种实现方式:

1.创建一个父类,在父类中实现抖动相关的代码。 2.创建一个UIView拓展,在拓展中实现相关代码。 3.使用面向协议,在协议中实现相关代码。 从实现方式来看,第一种和第二种也是可以实现功能需求的开发。

分析方法一

方法一会存在一个代码耦合性的问题,如果在父类按钮,后续又添加了其他方法。 比如旋转又或者其他功能,那么就导致代码冗余在了父类当中,也会对其他调用的人,造成理解的歧义。 而且如果这新功能,需要引用其他的类来实现一些功能,那么在BaseView中的代码就非常冗余,大量的代码堆积。

分析方法二

方法二相对父类添加,确实降低了代码的耦合性问题。但是由于是拓展方法,就导致了在任何一个UIView,都通过 .xxxx 的方式调用拓展方法,即使他们根本不需要这个功能。如果被其他开发误用,那么也增加了其他开发者的困惑。

分析方法三

利用 Swift的面向协议编程,我们就可以轻松的解决这个问题。 定义好该协议并且默认实现了颤抖的方法以后,如果控件想实现颤抖的功能,比如当点赞按钮。 此时我们只需要让该按钮遵守这个协议即可,其他什么都不需要操作了,这样就可以调用 lkk_shakeable()方法即可。

import UIKit
protocol LKKShakeable{
}
extension LKKShakeable where Self : UIView
{
    func lkk_shakeable()
    {
        translatesAutoresizingMaskIntoConstraints = true
        let posLbl = layer.position
        let animation = CABasicAnimation(keyPath: "position")
        animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
        animation.fromValue = NSValue(cgPoint:CGPoint(x: posLbl.x - 10, y: posLbl.y) )
        animation.fromValue = NSValue(cgPoint:CGPoint(x: posLbl.x + 10, y: posLbl.y) )
        animation.autoreverses = true
        animation.duration = 0.04
        animation.repeatCount = 6
        layer.add(animation, forKey: nil)
        translatesAutoresizingMaskIntoConstraints = false
    }
}

Example:

class loginButton :UIButton , LKKShakeable{}

处理Xib文件加载

加载Xib文件的这坨代码就会经常重复Copy,那么我们可以利用面向协议的思想抽取出来,使用的地方只需要遵守该协议即可,使用起来更加灵活.

import UIKit
protocol NibEnable {
}
extension NibLoadable where Self : UIView {
    static func loadFromNib(_ nibname : String? = nil) -> Self {
        let loadName = nibname == nil ? "(self)" : nibname!
        return Bundle.main.loadNibNamed(loadName, owner: nil, options: nil)?.first as! Self
    }
}
全部评论

相关推荐

昨天 16:48
上海大学 Java
忙碌的芝士选钝角:招侦探?
点赞 评论 收藏
分享
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务