面向协议
面向协议
(Protocol Oriented Programming, 简称POP
),是swift
的一种编程范式,Apple于2015年WWDC上提出,在swift的标准库中,能见到大量POP的影子,同时swift也是一门面向对象
的编程语言(Object Oriented Programming, 简称OOP
), 在项目开发中这两种编程规范相辅相成
。
回顾OOP
OOP
的三大特性封装、继承、多态
。
继承
的经典使用场合:当多个类具有很多共性(方法、属性等)时,可以将这些共性抽取到一个父类当中,最后这些类继承这个父类。
但有时候使用OOP
并不能很好的解决问题,比如:
// 继承自 UIViewController
class BVC: UIViewController {
func run() {
print("run")
}
}
// 继承自 UITableViewController
class DVC: UITableViewController {
func run() {
print("run")
}
}
BVC
和DVC
具有公共的方法run
, 但由于是继承自不同的父类, 所以基于OOP
我们可以采用:
-
第一种做法:
将run
方法放到另外一个对象A
当中,然后BVC
和DVC
拥有对象A
属性(相当于将方法抽离到对象A当中,然后BVC和DVC分别去调用这个A对象)
弊端
: 多了一些额外的依赖关系(两个类都需要依赖于对象A) -
第二种做法:
将run
方法增加到UIViewController
的扩展方法
当中(因为UITableViewController继承自UIViewController, 所以可以这么干)
弊端
: 给UIViewController
增加扩展方法
会影响到其他所有的UIViewController的子类,但实际上run
方法我只想给这两个类(BVC和DVC)使用 -
第三种做法:
将,run
方法抽取到新的父类,采用多继承oc
和swift
不支持多继承
, C++支持多继承
弊端
: 会增加程序设计复杂度,产生菱形继承
等问题(内存会产生冗余)
POP的解决方案
以上问题,采用POP
可完美解决问题,
// 定义协议
protocol Runnable {
func run()
}
// 给协议增加扩展方法
extension Runnable {
func run() {
print("run")
}
}
// BVC和DVC只需要遵守Runnable协议即可
class BVC: UIViewController, Runnable {
}
class DVC: UIViewController, Runnable {
}
所以面向协议编程,在某些时候也是一种很好的解决问题的方式.
利用协议实现前缀效果
在开发当中,通常都会给一些类扩展一些方法,比如假设要对一个字符串增加一个计算只包含有数字的长度:
extension String {
var numberCount: Int {
var count = 0
for c in self where ("0"..."9").contains(c) {
count += 1
}
return count
}
}
var word = "1234qwer5678"
print(word.numberCount)
但这样做是对系统的String类增加了一个numberCount的方法,有冲突的风险,完美的做法是在类前面加一个前缀用来区分,直接去更改扩展的方法名也是可以的,但通过协议去扩展实现更加优雅。
struct MT {
// 由于MT是自己定义的结构体,需要保存传入的字符串值以进行计算
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 mt: MT {
// 实例化MT的时候,将字符串内容传递到MT当中保存
return MT(self)
}
}
var word = "1234qwer5678"
// 调用方式是通过命名空间了
print(word.mt.numberCount)
以上就是通过给String
类型增加了一个MT
的命名空间,但假设要给数组、或者其他类型增加一个方法呢?
更优雅的实现方式是使用泛型
去实现:
// 通过泛型去实现扩展方法
struct MT<Base> {
var base: Base;
init(_ base: Base) {
self.base = base;
}
}
// 给String类增加一个MT的扩展
extension String {
var mt: MT<String> {
// 实例化MT的时候,将字符串内容传递到MT当中保存
return MT(self)
}
}
// 给MT增加一个numberCount的扩展, (当Base是String的时候)
extension MT where Base == String {
var numberCount: Int {
var count = 0
for c in base where ("0"..."9").contains(c) {
count += 1
}
return count
}
}
// string类
var word = "1234qwer5678"
// 通过命名空间的形式去调用
print(word.mt.numberCount) // 输出: 8
// 自定义类
class MyClass: NSObject {
var name: String;
var age: Int;
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
// 给自定义类增加自定义扩展
extension MyClass {
var mt: MT<MyClass> {
// 实例化MT的时候,将字符串内容传递到MT当中保存
return MT(self)
}
}
extension MT where Base: MyClass {
var desc: String {
return "My name is \(base.name), \(base.age) years old"
}
}
// 自定义的类
var person = MyClass(name: "jack ma", age: 46)
print(person.mt.desc) // 输出: My name is jack ma, 46 years old
这样写就可以更加优雅的实现自定义前缀了,而且使用到了泛型,对于任何类都是支持的
增加方法、类方法
struct MT<Base> {
let base: Base
init(_ base: Base) {
self.base = base
}
}
protocol MTCompatible {
}
extension MTCompatible {
static var mt: MT<Self>.Type {
get { MT<Self>.self }
set {}
}
var mt: MT<Self> {
get { MT(self) }
set {}
}
}
extension String: MTCompatible { }
extension MT where Base == String {
func numberCount() -> Int {
var count = 0
for c in base where ("0"..."9").contains(c) {
count += 1
}
return count
}
}
let num = "123qwer123"
print(num.mt.numberCount())
// 自定义类
class MyClass: NSObject {
var name: String;
var age: Int;
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
class Student: MyClass {
}
extension MyClass: MTCompatible {
}
extension MT where Base: MyClass {
func run() {
print("run method")
}
static func test() {
print("类方法")
}
}
let myclass = MyClass(name: "jack ma", age: 46)
let student = Student(name: "grace", age: 12)
myclass.mt.run()
student.mt.run()
// 只能打印类方法
MyClass.mt.test()
Student.mt.test()
利用协议实现类型判断
比如,我们在实现一个判断数据
是否是数组
时候,会这么写:
func isArray(_ array: Any) -> Bool {
return array is [Any]
// 这样写也是可以的
// return array is Array<Any>
}
print(isArray(2))
print(isArray("1"))
print(isArray(NSArray()))
print(isArray(NSMutableArray()))
print(isArray([1, "2"]))
在判断一个类型
是否是数组类型
的时候,会这么写
// 判断类型
func isArrayType(_ type: Any.Type) -> Bool {
return type is [Any].Type
|| type is NSArray.Type
|| type is NSMutableArray.Type
}
print(isArrayType(String.self))
print(isArrayType([Any].self))
print(isArrayType(NSArray.self))
print(isArrayType(NSMutableArray.self))
但利用协议
的话就非常简单了!
// 判断类型
protocol ArrayType {
}
extension Array: ArrayType {
}
extension NSArray: ArrayType {
}
func isArrayType(_ type: Any.Type) -> Bool {
return type is ArrayType.Type
}
print(isArrayType(String.self))
print(isArrayType([Any].self))
print(isArrayType(NSArray.self))
print(isArrayType(NSMutableArray.self))
本文首次发布于 孙忠良 Blog, 作者 [@sunzhongliang] , 转载请保留原文链接.