一、面向协议编程(POP)
-
1.1、面向协议编程(Protocol Oriented Programming,简称POP)
- 面向协议编程是Swift的一种编程范式, Apple于2015年WWDC提出
- 在Swift的标准库中,能见到大量POP的影子。
1.2、Swift也是一门面向对象的编程语言(Object Oriented Programming,简称OOP) ,在Swift开发中,OOP和POP是相辅相成的,任何一方并不能取代另一方,POP能弥补OOP一些设计上的不足。
-
1.3、回顾 POP
- OOP的三大特性:封装、继承、多态
- 继承的经典使用场合:当多个类(比如A、B、C类)具有很多共性时,可以将这些共性抽取到一个父类中(比如D类),最后A、B、C类继承D类
-
1.4、OOP的不足之处
-
但有些问题,使用OOP并不能很好解决,比如 p如何将BVC、DVC 的公共方法run 抽取出来?
class BVC: UIViewController { func run() { print("run") } } class DVC: UITableViewController { func run() { print("run") } }
-
基于OOP想到的一些解决方案?
- (1)、将 run 方法放到另一个对象A中,然后BVC、DVC拥有对象A属性 ,不足之处:多了一些额外的依赖关系
- (2)、将 run 方法增加到UIViewController分类中,不足之处:UIViewController会越来越臃肿,而且会影响它的其他所有子类
- (3)、将 run 方法抽取到新的父类,采用多继承?(C++支持多继承),不足之处:会增加程序设计复杂度,产生菱形继承等问题,需要开发者额外解决
-
-
1.5、针对1.4 不足之处比较好的解决方案:定义协议,遵守协议,实现
run
方法protocol Runnable { func run() } extension Runnable { func run() { print("run") } } class BVC: UIViewController, Runnable {} class DVC: UITableViewController, Runnable {}
-
1.6、POP的注意点
- 优先考虑创建协议,而不是父类(基类)
- 优先考虑值类型(struct、enum),而不是引用类型(class) n
- 巧用协议的扩展功能
- 不要为了面向协议而使用协议
二、利用协议实现自定义前缀效果
-
2.1、创建一个
JKPOP
结构体,泛型类型struct JKPOP<Base> { let base: Base init(_ base: Base) { self.base = base } }
-
2.2、创建协议
JKPOPCompatible
,扩展协议,给其增加jk
的类属性和实例属性protocol JKPOPCompatible {} extension JKPOPCompatible { static var jk: JKPOP<Self>.Type { get{ JKPOP<Self>.self } set {} } var jk: JKPOP<Self> { get { JKPOP(self) } set {} } }
-
2.3、前缀
jk
的使用一,比如计算一个字符串中数字的个数,如下-
(1)、创建 String 分类,并遵守
JKPOPCompatible
协议extension String: JKPOPCompatible {} extension NSString: JKPOPCompatible {}
-
(2)、给 JKPOP 增加扩展,并判断
Base泛型
的类型extension JKPOP 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 } }
-
(3)、使用
let name = "测试富文本123" name.jk.numberCount() // 打印结果是:3
-
-
2.4、Base 类
class Person {} class Student: Person {} extension Person: JKPOPCompatible {} extension JKPOP where Base: Person { func run() {} static func test() {} } Person.jk.test() Student.jk.test() let p = Person() p.jk.run() let s = Student() s.jk.run()
我们可以看到:类和实例对象都可以调用 jk,并且类只能调用类方法,实例属性只能调用实例方法
-
2.5、Base:协议
extension String: JKPOPCompatible {} extension NSString: JKPOPCompatible {} extension JKPOP 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 } } var s1: String = "123fdsf434" var s2: NSString = "123fdsf434" var s3: NSMutableString = "123fdsf434" print(s1.jk.numberCount()) print(s2.jk.numberCount()) print(s3.jk.numberCount())
-
2.6、利用协议实现类型判断
-
不使用协议判断是不是数组
func isArray(_ value: Any) -> Bool { value is [Any] } isArray( [1, 2] ) isArray( ["1", 2] ) isArray( NSArray() ) isArray( NSMutableArray() )
-
使用协议判断是不是数组
protocol ArrayType {} extension Array: ArrayType {} extension NSArray: ArrayType {} func isArrayType(_ type: Any.Type) -> Bool { type is ArrayType.Type } isArrayType([Int].self) // true isArrayType([Any].self) // true isArrayType(NSArray.self) // true isArrayType(NSMutableArray.self) // true print(isArrayType(String.self)) // false
-