面向協(xié)議編程(Protocol Oriented Programming顶别,簡(jiǎn)稱POP)
是Swift的一種編程范式些阅,Apple于2015年WWDC踢出
在Swift的標(biāo)準(zhǔn)庫中狐粱,能見到大量POP的影子
同時(shí)舀寓,Swift也是一門面向?qū)ο蟮木幊陶Z言(Objec Oriented Programming,簡(jiǎn)稱OOP)
在Swift開發(fā)中肌蜻,OOP和POP是相輔相成的互墓,任何一方并不能取代另一方
P- OP能彌補(bǔ)OOP一些設(shè)計(jì)上的不足
回顧OOP
- OOP的三大特性:封裝、繼承蒋搜、多態(tài)
- 繼承的經(jīng)典使用場(chǎng)合:
- 當(dāng)多個(gè)類(比如A篡撵、B、C類)具有很多共性時(shí)豆挽,可以將這些共性抽取到一個(gè)父類中(比如D類)育谬,最后A、B帮哈、C類繼承D類

OOP的不足
- 但是有些問題膛檀,使用OOP并不能很好解決,比如:
- 如何將BVC娘侍、DVC的公共方法run抽取出來咖刃?
class BVC: UIViewController {
func run() {
print("run")
}
}
class DVC: UITableViewController {
func run() {
print("run")
}
}

- 基于OOP想到的一些解決方案?
- 將run方法翻到另一個(gè)對(duì)象A中私蕾,然后BVC僵缺、DVC擁有對(duì)象A屬性(多了一些額外的依賴關(guān)系)
- 將run方法添加到UIViewController分類中(UIViewController會(huì)越來越臃腫,而且會(huì)影響它其他的所有子類)
- 將run方法抽取到薪的父類踩叭,采用多繼承磕潮?(C++支持多繼承)(會(huì)增加程序設(shè)計(jì)復(fù)雜度,產(chǎn)生菱形繼承等問題容贝,需要開發(fā)者額外解決)
POP的解決方案
protocol Runnable {
func run()
}
extension Runnable {
func run() {
print("run")
}
}
class BVC: UIViewController, Runnable {}
class DVC: UIViewController, Runnable {}
再舉例

POP的注意點(diǎn)
- 優(yōu)先考慮創(chuàng)建協(xié)議自脯,而不是父類(基類)
- 優(yōu)先考慮值類型(struct、enum)斤富,而不是引用類型(class)
- 巧用協(xié)議的擴(kuò)展功能
- 不要為了面向協(xié)議而使用協(xié)議
利用協(xié)議實(shí)現(xiàn)前綴效果
struct My {
var string: String
init(_ string: String) {
self.string = string
}
var numberCount: Int {
var count = 0
for c in string where("0"..."9").contains(c) {
count += 1
}
return count
}
}
extension String {
var my: My { return My(self) }
}
print("123kkk".my.numberCount) // 3
- 泛型優(yōu)化 - 不用添加n多個(gè)不同類型的存儲(chǔ)屬性
struct My<Base> {
var base: Base
init(_ base: Base) {
self.base = base
}
}
- 擴(kuò)展優(yōu)化 - 給具體類型擴(kuò)展實(shí)例方法和實(shí)例屬性
extension My where Base == String {
var numberCount: Int {
var count = 0
for c in base where("0"..."9").contains(c) {
count += 1
}
return count
}
}
- 擴(kuò)展類型方法和屬性
class Person {}
extension Person {
var my: My<Person> { My(self) }
static var my: My<Person>.Type { My(Person).self }
}
extension My where Base == Person { // 需要給對(duì)應(yīng)子類擴(kuò)充的話使用冒號(hào)膏潮,例如:Base: Person
func run() {
print("run")
}
static func test() {
print("test")
}
}
Person().my.run()
Person.my.test()
- 協(xié)議優(yōu)化 - 不用給每一個(gè)類型擴(kuò)展前綴屬性
protocol MyCompatible {}
extension MyCompatible {
var my: My<Self> { My(self) }
static my: My<Self>.Type { My<Self>.self }
}
class String {}
extension String: MyCompatible {}
- 最終版本
/// 前綴類型
struct My<Base> {
var base: Base
init(_ base: Base) {
self.base = base;
}
}
/// 利用協(xié)議擴(kuò)展前綴屬性
protocol MyCompatible {}
extension MyCompatible {
var my: My<Self> {
set {} // *** 為了擴(kuò)充mutating方法,所以不能寫只讀屬性
get { My(self) }
}
static var my: My<Self>.Type {
set {} // *** 為了擴(kuò)充mutating方法满力,所以不能寫只讀屬性
get { My<Self>.self }
}
}
/// 給字符串?dāng)U展功能
// 讓String擁有my前綴屬性
extension String : MyCompatible {}
// 給String前綴擴(kuò)展功能
extension My where Base == String {
var numberCount: Int {
var count = 0
for c in base where("0"..."9").contains(c) {
count += 1
}
return count
}
mutating func run() {}
static func test() {}
}
var str = "666"
str.my.run()
String.my.test()
Base的補(bǔ)充
- 擴(kuò)展可以擴(kuò)充子類class
class Person {}
class Student: Person {}
extension Person: MyCompatible {}
extension My where Base: Person {
func run() {}
static func test() {}
}
Person.my.test()
Student.my.test()
let p = Person()
p.my.run()
let s = Student()
s.my.run()
- 擴(kuò)展可以遵守共同協(xié)議
- 同時(shí)給String焕参、NSString和NSMutableString擴(kuò)展功能
extension String: MyCompatible {}
extension NSString: MyCompatible {}
extension My where Base: ExpressibleByStringLiteral {
var numberCount: Int {
var count = 0
for c in (base as! String) where("0"..."9").contains(c) {
count += 1
}
return count
}
mutating func run() {}
static func test() {}
}
var str1 = "123xxx"
var str2: NSString = "123xxx"
var str3: NSMutableString = "123xxx"
print(str1.my.numbserCount)
print(str2.my.numbserCount)
print(str3.my.numbserCount)
利用協(xié)議實(shí)現(xiàn)類型判斷
func isArray(_ value: Any) -> Bool {
// value is Array<Any>
value is [Any]
}
print(isArray([1, 2])) // true
print(isArray(["1", 2])) // true
print(isArray(NSArray())) // true
print(isArray(NSMutableArray())) // true
print(isArray("12312421")) // false
protocol ArrayType {}
extension Array: ArrayType {}
extension NSArray: ArrayType {}
func isArrayType(_ type: AnyType) -> Bool { type is ArrayType.Type }
isArrayType([Int].self)
isArrayType([Any].self)
isArrayType(NSArray.self)
isArrayType(NSMuatbleArray.self)