设计可观察对象

问题描述 投票:0回答:1

前言:这是关于反应式编程的设计问题。它旨在与语言无关,因此都是伪代码。我怀疑无论正确的答案是什么,在Rx / ReactiveCocoa / Combine之间同样适用。

我看到了3种设计观察对象的不同方式。每个人肯定都有优点/缺点,但是不清楚它们是什么。

  1. 您的对象可能具有didChange: Publisher<()>属性。订阅时,您会收到有关发布对象何时更改的通知,但实际上您并未了解有关更改或模型对象的新值的任何信息。这很容易用map修复:

    class Point: BindableObject {
        var x, y: Int
        var didChange: Publisher<()> { ... }
    }
    let object: Point = ...
    let streamOfPoints = object.didChange //  Publisher<()>
                                .map { _ in object }  // Publisher<Point>
    

    通过通知其他对象直接访问该对象,您可以简单地自己获取该对象的值。如果需要访问xy值的流,则也只需一个map调用即可。

    但是,似乎有一些问题。

    1. 这是额外的步骤
    2. 它需要您有权访问原始对象,因此仅使发布者四处转移是不够的。您必须传递(Point, Publisher<Point>)对,这似乎很麻烦。
    3. 它可能有正确性问题。例如,在触发didChange事件与访问该对象之间存在任何延迟,难道您读取的对象值可能比更改被触发时出现的值新吗?

      [这种方法是我最不喜欢的方法,但是有趣的是,这是Apple在BindableObject协议中采用其Combine框架的方法。我怀疑这可能与某种性能提升有关,因为在不需要的情况下不必在对象周围进行管道传输。是这样吗?

  2. 最明显的方法是流对象本身。 var didChange: Publisher<Point> { /* a publisher that emits self over time */ }。在解决我列出的3个问题的同时,这似乎达到了方法1的相同。我看不到方法1可以提供的任何价值。

  3. 您可以为对象的每个字段创建发布者:

    class Point: BindableObject {
        let x = PublishSubject<Int>
        let y = PublishSubject<Int>
    }
    

    这是更详细的信息,因此人们可以仅将自己所关注的领域归为自己。我不知道重量级订阅的情况如何,但仅更具体地订阅您关心的内容,可能会获得一些性能上的收益。这里有一个人为的例子,因为很难想到只需要知道一个点的x值或y值的情况。但是该原理仍然普遍适用。

    使用前两种方法之一访问xs和ys也是可能的,方法是将流映射到xs并进行重复数据删除(.map { $0.x }.distinct)。但这比像这样的直接订阅调用了更多的映射闭包,这可能会影响性能。

    此方法也可以与方法1或2结合使用,以在需要观察整个点对象时添加类型为var didChangePublisher<()>Publisher<Point>属性。

    这曾经导致很多API膨胀。在Rx中:

    1. 您可能拥有x: Value<Int>,并在各处使用x.value访问当前值
    2. 或者,您有var xObservable: Value<Int>var x: Int { xObservable.value },但这增加了很多API膨胀。

    3. 幸运的是,属性包装器通过基本实现后一种设计,而又无需显式添加所有计算出的属性(它们是为您生成的),就解决了这两个问题。

您能否就使用哪种模式提供一些指导?我怀疑那是“经验丰富”的东西之一,但是在反应式编程方面我还不存在。谢谢!

前言:这是关于反应式编程的设计问题。它旨在与语言无关,因此都是伪代码。我怀疑,无论正确的答案是什么,都会同样适用...

swift reactive-programming rx-swift reactive-cocoa combine
1个回答
0
投票

这个问题有很多需要考虑的地方,但我可能会说要选择选项2。

© www.soinside.com 2019 - 2024. All rights reserved.