快点射!(Swift Injection) 不带这样的!

导言

注入(Injection)在任何语言里都是非常有效的解耦利器。

请不要把上面的注入和注入攻击混淆起来,不要把本猫逼得变身成黑客猫 😉

本篇就带大家看看如何解决Swift中Injection的一个常见问题:

怎么解决泛型协议实体兼容性问题???

在这里插入图片描述

快点射(Swift Injection), 别想歪

如果你没有被上面那拗口一句 怎么解决泛型协议实体兼容性问题???

吓跑的话,那么恭喜你,坚持看下去你会觉得其实也没想象的那么难…

上帝说要有协议,所以协议来了:

protocol Power{
    func publisher() -> AnyPublisher<String,URLError>
}

什么人有Power? 超人算一个吧:

struct Superman: Power{
    func publisher() -> AnyPublisher<String, URLError> {
        // 极其复杂的逻辑,你不用管... ;)
    }
}

下面快在我们的Model中使用超人吧:

struct Model {
    private var powerMan:Power
    
    init(powerMan:Power = Superman()){
        self.powerMan = powerMan
    }
}

在这里插入图片描述

上面的powerMan就是一个注入(Injection)属性。
注意,之所以powerMan类型是Power协议而不是一个实体类型(比如Superman),就是为后面的注入铺路。

注入一个方便的地方是测试:

struct MockPower: Power {
    func publisher() -> AnyPublisher<String, URLError> {
      Just("Mock Power")
          .setFailureType(to: URLError.self)
          .eraseToAnyPublisher()
    }
}

现在我们可以在不改动Model的情况下,用一个MockPower来测试其逻辑:

let model = Model(powerMan: MockPower()) 

而不是正常逻辑中的:

let model = Model()

That’s Injection!!!

在这里插入图片描述

理想很骨感,现实很残酷

好吧,上面只是理想世界中的情况.

现实中可没那么简单!!!

回到Power协议:

protocol Power{
    func publisher() -> AnyPublisher<String,URLError>
}

注意返回发布者的错误类型,它指定了URLError!

这有问题么? 有!

没有我说毛线呢?

在这里插入图片描述

现实中遵从Power协议中错误类型,应该是由实体类型决定的!这意味着在这里不能限定死错误类型!

所以我们要将Power类型做如下修改:

protocol Power{
    associatedtype ErrorType:Error
    func publisher() -> AnyPublisher<String,ErrorType>
}

同理需要如下修改Superman类型:

struct Superman: Power {
    enum Error:Swift.Error{
        case krypton        // 超人怕氪星物质???
        case lover          // 超人担心女友???
    }
    
    func publisher() -> AnyPublisher<String, Self.Error> {
        // 复杂实现,不用你管... ;)
    }
}

你会说:

侬看,实体类型自定义错误也很容易嘛…

在这里插入图片描述

NO,NO… 你高兴的太早了,问题出在ViewModel里:

struct Model{
    private var powerMan: Power
}

在这里插入图片描述

Protocol ‘Power’ can only be used as a generic constraint because it has Self or associated type requirements

看到上面错误提示了吗???
协议含有一个泛型(这里叫associated type),这样的协议类型是不能直接作为generic constraint的哦,如上代码所示。

这样一来,无法完成Injection了…

在这里插入图片描述

没有办法了么?非也非也…

以其人之道,还治其人之身

泛型惹的祸,我们还靠泛型来救!

将ViewModel定义改成如下形式:

struct Model<S:Power>{
    private var powerMan: S
}

错误没有了,我们又可以愉快地继续了…

不过危机还没有完全解除,为了完成初始化Injection,我们还需要修改初始化器的代码:

struct Model<S:Power>{
    private var powerMan: S
    
    init(powerMan:S = Superman() as! S){
        self.powerMan = powerMan
    }
}

在设置初始化器的默认参数时,我们需要做一个类型强转.否则编译器还是得给你难堪。

不过最艰难的时刻已经过去了,我们现在又可以愉快地写Injection测试了:

struct MockPower: Power {
    func publisher() -> AnyPublisher<String, Error> {
        Just("Mock")
            .setFailureType(to: Error.self)
            .eraseToAnyPublisher()
    }
}

// 正经的Model
let model = Model<Superman>()
// 测试的Model
let testModel = Model(powerMan: MockPower())

这波操作稳中带皮,可以!

在这里插入图片描述

雷打不动的结语

照例还是要写总结…

Injection在大多数现代编程语言中都是不可忽视的重要模式,尤其是动态语言中,虽然Swift不能算是动态语言…

Swift到了5.1版本后,API已基本固定,但其中语法语义还是有可以优化的地方,让我们翘首以盼吧 😉

相信猪猫,没错哒…
在这里插入图片描述

大熊猫侯佩 CSDN认证博客专家 Swift Objective-C Xcode
非自由程序员,CSDN博客认证专家。
CSDN汇编板块版主, CSDN其他开发语言大版版主。

对App、以及Cocos2D、SpriteKit游戏开饶有兴趣。目前常用的语言是ObjC、Swift、Ruby等。不过看到编程艺术、ASM、逆向和C时依然欲罢不能。虽然不是,但喜欢黑客的思维和哲学,认为社会工程学很酷,但还没有实际用来撩过妹。
已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 精致技术 设计师:CSDN官方博客 返回首页
实付 39.90元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值