【问题标题】:Callback violates Inversion of Control回调违反控制反转
【发布时间】:2020-07-31 22:45:49
【问题描述】:

如果我对 IOC 的理解正确,这意味着您为驱动功能而配置的任何框架或模块都应该能够在需要时驱动您的代码(参考:Martin Flower IOC Blog)。这意味着控制是框架驱动的。

我正在浏览一个精彩的博客Future and Promises in Combine。一开始的博客说,通过回调或闭包处理异步行为违反了 IOC。我有点不同意这种说法,但只是想澄清一下我的理解。

场景:

我创建了一个TestFramework,它为框架用户处理一些功能。

public class TestFramework {

    typealias UpdatedFrameworkData = (FrameworkData?) -> Void

    var giveUpdatedValues: (() -> UpdatedFrameworkData)?

    init() { }

    private func someAction() {
        var updatedValuesCallback = giveUpdatedValues?()
        updatedValuesCallback = { [weak self] updatedFrameworkData in
            // Perform some action

        }
    }
}

现在我有一个使用这个框架的类

class FrameworkUser {
    let framework: TestFramework = TestFramework()
    var updatedValuesCallback: TestFramework.UpdatedFrameworkData?

    init() {
       setupBinding()
    }

    private func setupBinding() {
        updatedValuesCallback = framework.giveUpdatedValues?()
    }

    private func getUpdatedData(completion: @escaping TestFramework.UpdatedFrameworkData) {
        // Return some data
        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
            self.updatedValuesCallback?(self.getFrameworkData())
        }
    }

    private func getFrameworkData() -> FrameworkData? {
        nil
    }
}

框架有时可能需要处理更新的数据,而不是一些陈旧的数据。如果您观察我的框架仍然可以通过用户提供的侦听器控制何时获取更新数据的功能。用户通过异步回调向框架提供数据。

那么,在这种情况下,我是不是通过回调实现了 IOC,还是我错过了什么?

编辑:

我在上面的设置绑定代码中犯了一个小错误。用户仍然调用框架来提供更新的值。

我对上面的代码进行了以下修改,以发现通过回调对程序的异步处理确实违反了 IOC 原则。上面的setupBinding() 方法在绑定到框架回调的方式上是不正确的。应该是这样的:

private func setupBinding() {
        framework.giveUpdatedValues = {
            return getUpdatedData() // This won't work
        }
    }

为了遵循 IOC 原则,用户应将其代码绑定到框架回调。上面的代码根本行不通,因为你不能简单地从函数中返回一个转义闭包,而这个闭包将在稍后的某个时间轴上执行。

【问题讨论】:

  • 我认为第二个链接是说使用回调闭包实现的异步操作(也称为完成处理程序)违反了 IoC,而不是闭包本身。
  • @Sweeper 感谢您指出这一点。用适当的例子编辑了问题

标签: ios swift callback inversion-of-control combine


【解决方案1】:

闭包和协议在语义上是相同的,即使它们在语法上不同。如果闭包违反 IOC,那么协议也是如此。由于 IOC 的经典示例使用协议,这意味着在语义上相同的闭包不会违反它。

为了表明它们是相同的:

protocol FrameworkDelegate { 
    func myFunc(data: Int)
}

class Framework {
    weak var delegate: FrameworkDelegate?

    func example() {
        delegate?.myFunc(5)
}

class Framework {
    var delegate: ((Int) -> Void)?

    func example() {
        delegate(5)
    }
}

上面两个代码示例之间的唯一区别是确保delegate 不会取消初始化的对象。在这两种情况下,Framework 的用户定义发生什么,而框架类型决定何时调用“委托方法”。

【讨论】:

  • 正如@sweeper 在其中一个 cmets 中指出的那样,作者在博客中谈到了使用回调的异步编程。在我在问题中提到的代码中,您可以观察到未来的回调giveUpdatedValues: (() -> UpdatedFrameworkData)? 返回一个回调到提供更新数据的框架。此回调由用户端的异步进程调用。我认为作者认为用户方对updatedValuesCallback () 的调用违反了IOC,因为用户在数据可用时再次调用框架。请CMIIW。
  • 有趣的是,在本次演讲 (frontendmasters.com/courses/rethinking-async-js/…) 中,演讲者断言异步回调 do 会反转控制,这就是它们的问题所在!他解释了为什么他们颠倒控制,而您引用的文章只是提出主张而根本没有任何解释。
  • Martin Fowler 的文章也使用回调来显示控制反转。当然,他是在 UI 引擎的上下文中执行此操作的,但如果 Tk 通过网络发送请求并在完全不同的计算机上显示输入窗口,这是否意味着 IOT 突然被相同的代码违反了?我认为不会。
猜你喜欢
  • 2012-07-24
  • 2015-07-10
  • 1970-01-01
  • 1970-01-01
  • 2010-09-20
  • 2011-04-11
  • 1970-01-01
  • 2011-08-05
  • 2011-07-06
相关资源
最近更新 更多