美文网首页
使用协议实现前缀效果

使用协议实现前缀效果

作者: 得_道 | 来源:发表于2020-11-08 16:27 被阅读0次

    使用ReactiveCocoa过应该见过很多这样的代码,例:

    textField.reactive.textValues
    

    很多的系统类扩展了很多方法,且前面都有一个reactive的前缀效果。
    这是怎么做到的呢?

    分析

    首先,看一个例子:

    var string = "123"
    print(string.mj.numbercount())
    

    实例对象后面是.调用,所以肯定是为系统类扩展了一个属性mj,并且mj后面又调用了方法numbercount()
    所以是为String类扩展了一个mj实例对象属性,假定是MJ类型, 且MJ存在一个numbercount()方法

    struct MJ {
        func numberCount() -> Int {
            return 0
        }
    }
    
    extension String {
        var mj: MJ { MJ() }
    }
    
    print("123".mj.numberCount())
    

    是不是已经出来了前缀的效果?但这只是开始...

    基础版实现

    我们要打印字符串个数,要改下numberCount()里的实现代码

    struct MJ {
        func numberCount() -> Int {
            var count = 0
            for c in self where ("0"..."9").contains(c) {
                count += 1
            }
            return count
        }
    }
    

    但这里会报错


    image.png

    因为这里要遍历的是字符串,但我们怎么拿到这个要遍历的字符串呢?
    需要改造下MJ这个类,增加一个string属性,并增加一个构造方法init(_ string: String),改造后如下

    struct MJ {
        var string: String
        init(_ string: String) {
            self.string = string
        }
        func numberCount() -> Int {
            var count = 0
            for c in string where ("0"..."9").contains(c) {
                count += 1
            }
            return count
        }
    }
    

    然后再改变Stringmj属性, 在初始化mj时将self传进去

    extension String {
        var mj: MJ { MJ(self) }
    }
    

    完整代码如下:

    struct MJ {
        var string: String
        init(_ string: String) {
            self.string = string
        }
        func numberCount() -> Int {
            var count = 0
            for c in string where ("0"..."9").contains(c) {
                count += 1
            }
            return count
        }
    }
    
    extension String {
        var mj: MJ { MJ(self) }
    }
    
    print("123".mj.numberCount())
    
    进阶版实现

    假如不只给String增加mj前缀呢?还想给其它类,例如给Person增加mj前缀,也增加一个run()方法,怎么做呢?
    首先给Person扩展一个mj属性

    class Person {}
    extension Person {
        var mj: MJ { MJ(self) }
    }
    

    MJ类要增加一个Person属性吗?

    struct MJ {
        var string: String
        var person: Person
        
        init(_ string: String) {
            self.string = string
        }
        func numberCount() -> Int {
            var count = 0
            for c in string where ("0"..."9").contains(c) {
                count += 1
            }
            return count
        }
    
        func run() {
            print("run")
        }
    }
    

    那要很多类增加前缀,岂不是要在MJ类增加很多属性?
    当然不是这样了。并且怎么分离关于StringnumberCount()Personrun()的方法呢?
    配角登场 - 泛型
    MJ进行泛型改造

    struct MJ<Base> {
        var base: Base
        
        init(_ base: Base) {
            self.base = base
        }
    }
    

    StringPerson改造

    extension String {
        var mj: MJ<String> { MJ(self) }
    }
    
    extension Person {
        var mj: MJ<Person> { MJ(self) }
    }
    

    然后把MJ的方法,该放到MJ扩展里

    extension MJ where Base == String {
        func numberCount() -> Int {
            var count = 0
            for c in base where ("0"..."9").contains(c) {
                count += 1
            }
            return count
        }
    }
    
    extension MJ where Base: Person {
        func run() {
            print("run")
        }
    }
    

    上面的方法都是实例方法如果,要怎么类方法怎么做呢?
    那么就要增加一个static类型的mj,同时在MJ的扩展里增加static方法

    extension String {
        var mj: MJ<String> { MJ(self) }
        
        static var mj: MJ<String>.Type { MJ.self }
    }
    
    extension MJ where Base == String {
        func numberCount() -> Int {
            var count = 0
            for c in base where ("0"..."9").contains(c) {
                count += 1
            }
            return count
        }
        
        static func test() {
            print("test")
        }
    }
    

    完整代码如下:

    class Person {}
    
    struct MJ<Base> {
        var base: Base
        
        init(_ base: Base) {
            self.base = base
        }
    }
    
    extension String {
        var mj: MJ<String> { MJ(self) }
        
        static var mj: MJ<String>.Type { MJ.self }
    }
    
    
    extension Person {
        var mj: MJ<Person> { MJ(self) }
    
        static var mj: MJ<String>.Type { MJ.self }
    }
    
    extension MJ where Base == String {
        func numberCount() -> Int {
            var count = 0
            for c in base where ("0"..."9").contains(c) {
                count += 1
            }
            return count
        }
        
        static func test() {
            print("test")
        }
    }
    
    extension MJ where Base: Person {
        func run() {
            print("run")
        }
    }
    
    高级版实现

    在上面的实现过程中还有存在重复的代码,如下;
    这两个类的mj属性是重复的,以后要增加其他类的话,还要复制?粘贴?

    extension String {
        var mj: MJ<String> { MJ(self) }
        
        static var mj: MJ<String>.Type { MJ.self }
    }
    
    extension Person {
        var mj: MJ<Person> { MJ(self) }
    
        static var mj: MJ<String>.Type { MJ.self }
    }
    

    这时就要用协议抽取这个公共部分, 让其他类遵循该协议

    protocol MJCompatible {}
    extension MJCompatible {
        
        var mj: MJ<Self> { MJ(self) }
        
        static var mj: MJ<Self>.Type { MJ.self }
    }
    
    extension String: MJCompatible {}
    
    extension Person: MJCompatible {}
    

    注:这里还有个细节需要注意
    假如在扩展里增加一个mutating方法

    extension MJ where Base == String {
        func numberCount() -> Int {
            var count = 0
            for c in base where ("0"..."9").contains(c) {
                count += 1
            }
            return count
        }
        
        static func test() {
            print("test")
        }
        
        mutating func run() {
            
        }
    }
    
    

    调用试试

    image.png
    发现报错,因为mutating意思是要修改MJ结构体里的东西,但mj是实例是作为String的只读的计算属性,所以要在协议MJCompatible的属性作如下更改,将只读属性改为可读,可写,但set什么也不做
    extension MJCompatible {
        
        var mj: MJ<Self> {
            set {}
            
            get { MJ(self) }
        }
        
        static var mj: MJ<Self>.Type {
            set {}
            
            get { MJ.self }
        }
    }
    

    完整终极代码如下:

    class Person {}
    
    struct MJ<Base> {
        var base: Base
        
        init(_ base: Base) {
            self.base = base
        }
    }
    
    protocol MJCompatible {}
    extension MJCompatible {
        
        var mj: MJ<Self> {
            set {}
            
            get { MJ(self) }
        }
        
        static var mj: MJ<Self>.Type {
            set {}
            
            get { MJ.self }
        }
    }
    
    extension String: MJCompatible {}
    
    extension Person: MJCompatible {}
    
    extension MJ where Base == String {
        func numberCount() -> Int {
            var count = 0
            for c in base where ("0"..."9").contains(c) {
                count += 1
            }
            return count
        }
        
        static func test() {
            print("test")
        }
        
        mutating func run() {
            
        }
    }
    
    extension MJ where Base: Person {
        func run() {
            print("run")
        }
    }
    

    相关文章

      网友评论

          本文标题:使用协议实现前缀效果

          本文链接:https://www.haomeiwen.com/subject/aqeqbktx.html