本文主要认识面向协议编程和面向对象编程的优缺点,以及如何实现面向协议编程。
主要内容:
- 面向协议的认识
- 面向协议的使用
1、 认识
面向协议编程( Protocol Oriented Programming ,简称POP),Swift中既可以面向对象,也可以面向协议,在Swift开发中两者是相辅相成的。
在Swift的标准库中,能见到大量POP的影子,因为Swift协议有扩展功能,可以支持更强大的功能。
因此在Swift中大量使用结构体、使用协议,而不是类
2、 扩展功能
2.1 OOP的不足
代码:
/*
1、OOP的不足
*/
func test21() {
class BVC: UIViewController {
func run() {
print("run")
}
}
class DVC: UITableViewController {
func run() {
print("run")
}
}
}
/*
POP的解决方案,通过遵守协议来实现
*/
protocol Runnable {
func run()
}
extension Runnable {
func run() {
print("run")
}
}
func test22() {
class BVC: UIViewController, Runnable {}
class DVC: UITableViewController, Runnable {}
}
说明:
- 这里如何将两个方法抽取出来,基于OOP有这么几种解决方案
- 将run方法放到另一个对象A中,然后BVC、DVC拥有对象A属性。但是这样会多增加额外的依赖关系
- 将run方法统一增加到UIViewController分类中。但是UIViewController会越来越臃肿,而且会影响它的其他所有子类
- 采用多继承将run方法抽取到新的父类,但是会增加程序设计复杂度,产生菱形继承等问题,需要开发者额外解决
- 因此这种情况下使用POP,增加一个声明run方法的协议,BVC和DVC都遵守该协议就可以解决。
2.2 利用协议给字符串扩展一个功能
通过给字符串扩展一个功能为案例进行说明协议在实际开发中的作用,最终通过协议可以给任意的类型扩展任意的功能,有很好的封装性和扩展性
代码:
//结构体基类
struct WY<Base> {
let base: Base
init(_ base: Base) {
self.base = base
}
}
//协议
protocol WYCompatible {}
//给协议扩展两个属性,一个是类一个是对象
extension WYCompatible {
static var WY: WY<Self>.Type {
get { WY<Self>.self }
set {}
}
var WY: WY<Self> {
get { WY(self) }
set {}
}
}
extension String: WYCompatible {}
extension NSString: WYCompatible {}
//给WY结构体扩展一个方法numberCount
extension WY where Base: ExpressibleByStringLiteral {
func numberCount() -> Int {
let string = base as ! String
var count = 0
for c in string where ("0"..."9").contains(c) {
count += 1
}
return count
}
}
func test21() {
var s1: String = "123fdsf434"
var s2: NSString = "123fdsf434"
var s3: NSMutableString = "123fdsf434"
print(s1.WY.numberCount())
print(s2.WY.numberCount())
print(s3.WY.numberCount())
}
说明:
- 给字符串设置协议,给协议增加扩展,扩展里添加两个变量WY
- 而在WY结构体中扩展方法。
- 为了扩展的类型更加通用,结构体中使用泛型
- 为了给不同的泛型增加不同的方法,给结构体使用扩展增加方法
- 给结构体增加类型方法,这里注意类型变量的设置。
- 此处给NSString也遵守MJCompatible协议,这样它也就可以拥有了这个功能
注意:
1、为了可以给任意类型增加额外功能,可以将该功能抽取到一个结构体中去执行
2、为了给类型增加任意功能,可以将功能放到结构体的扩展中,并且遵守某个协议
3、 判断类型
以判断Array类型为例来说明
代码:
//对象的类型判断
func isArray(_ value: Any) -> Bool {
value is [Any]
}
print(isArray( [1, 2] ))
print(isArray( ["1", 2] ))
print(isArray( NSArray() ))
print(isArray( NSMutableArray() ))
//类的类型判断,需要添加协议,只要都遵守ArrayType协议就认为是数组
protocol ArrayType {}
extension Array: ArrayType {}
extension NSArray: ArrayType {}
func isArrayType(_ type: Any.Type) -> Bool {
type is ArrayType.Type
}
print(isArrayType([Int].self))
print(isArrayType([Any].self))
print(isArrayType(NSArray.self))
print(isArrayType(NSMutableArray.self))
说明:
- 对象判断很简单,只需要判断类型即可
- [Any]表示任何类型的数组,因此它就可以表示任意的数组
- 注意这里可以判断NSArray和NSMutableArray
- 对于类型判断无法通过元类型来判断,只能通过协议判断
- 因为[Any].type与[Int].type表示为两个不同的类型,无法进行判断
- 因此对于类型的判断,可以通过判断数组所遵守的协议
- 所有的数组均会遵守ArrayType协议,所以可以通过它来判断
4、总结
- 优先考虑创建协议,而不是父类(基类)
- 优先考虑值类型( struct、 enum ),而不是引用类型( class)
- 面向协议编程,优先考虑使用协议,而不是父类
- 需要巧用协议扩展
- 不要为了面向协议而使用协议