前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >简单的Swift函数的依赖注入

简单的Swift函数的依赖注入

作者头像
莫空9081
发布2021-03-01 12:42:43
发布2021-03-01 12:42:43
98000
代码可运行
举报
文章被收录于专栏:iOS 备忘录iOS 备忘录
运行总次数:0
代码可运行

简单的Swift函数的依赖注入

本文是翻译,原文链接:Simple Swift dependency injection with functions

依赖注入是一种很好的解耦代码的手段,使代码变得易于测试。比起来对象自己创建自己的依赖,从外部注入,使得我们可以设置不同的场景————例如在生产中 vs 在测试中。

在Swift中,大多数时候,我们用协议来实现依赖注入。例如,我们写一个简单的卡片游戏,用Randomizer(随机性发生器)画一个随机的卡片,如下所示:

代码语言:javascript
代码运行次数:0
复制
class CardGame {
    private let deck: Deck
    private let randomizer: Randomizer

    init(deck: Dec, randomizer: Randomizer = DefaultRandomizer()) {
        self.deck = deck
        self.randomizer = randomizer
    }

    func drawRandomCard() -> Card {
        let index = randomizer.randomNumber(upperBound: deck.count)
        let card = deck[index]
        return card
    }

}

上面的例子中,可以看到,我们在CardGame的初始化中,注入了一个Randomizer,用于在绘制时生成一个随机的index。为了使API易于使用,在没有给定randomizer时,我们还给它赋值了一个默认值————DefaultRandomizer。协议和默认实现如下:

代码语言:javascript
代码运行次数:0
复制
protocol Randomizer {
    func randomNumber(upperBound: UInt32) -> UInt32
}

class DefaultRandomizer: Randomizer {
    func randomNumber(upperBound: UInt32) -> UInt32 {
        return arc4random_uniform(upperBound)
    }
}

当我们设计的API非常复杂时,用协议实现依赖注入是非常好的。但是,当只有简单的目的(只需要一个简单的方法),用函数来实现可以减少复杂度。

上面的DefaultRandomizer本质上是arc4random_uniform的封装,所以为什么不试着通过传递一个函数类型来实现依赖注入,如下所示:

代码语言:javascript
代码运行次数:0
复制
class CardGame {
    typealias Randomizer = (UInt32) -> UInt32

    private let deck: Deck
    private let randomizer: Randomizer

    init(deck: Deck, randomizer: @escaping Randomizer = arc4random_uniform) {
        self.deck = deck
        self.randomizer = randomizer
    }

    func drawRandomCard() -> Card {
        let index = randomizer(deck.count)
        let card = deck[index]
        return card
    }
}

我们把Randomizer从协议变为简单的typealias,并且把arc4random_uniform函数直接做为randomizer的默认参数。再也不需要默认实现的类,同时还可以轻易mock测试randomizer:

代码语言:javascript
代码运行次数:0
复制
class CardGameTests: XCTestCase {
    func testDrawingRandomCard() {
        var randomizationUpperBound: UInt32?

        let deck = Deck(cards: [Card(value: .ace, suite: .spades)])

        let game = Cardgame(deck: deck, randomizer: { upperBound in
            // Capture the upper bound to be able to assert it later
            randomizationUpperBound = upperBound

            // Return a constant value to remove randomness from out test, making it run consistently
            return 0
        })

        XCTAssertEqual(randomizationUpperBound, 1)
        XCTAssertEqual(game.drawRandomCard(), Card(value: .ace, suite: .spades))
    }
}

我个人特别喜欢这种技术,因为可以写更少的代码,易于理解(直接把函数放在初始化方法中),同时还能实现依赖注入。

你怎么看?

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2018-12-062,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简单的Swift函数的依赖注入
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档